/[osn-commons]/trunk/uar/uar.c
ViewVC logotype

Diff of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 35 by rakinar2, Thu Aug 8 16:57:39 2024 UTC revision 36 by rakinar2, Thu Aug 8 19:13:26 2024 UTC
# Line 27  Line 27 
27  #endif  #endif
28    
29  const unsigned char UAR_MAGIC[] = { 0x99, 'U', 'A', 'R' };  const unsigned char UAR_MAGIC[] = { 0x99, 'U', 'A', 'R' };
30    const unsigned int UAR_MAX_SUPPORTED_VERSION = 0x01;
31    
32  struct uar_header  struct uar_header
33  {  {
# Line 51  struct uar_file Line 52  struct uar_file
52          {          {
53              char *loc;              char *loc;
54              uint64_t loclen;              uint64_t loclen;
55          } linkinfo;          } link;
56      } data;      } data;
57      mode_t mode;      mode_t mode;
58      time_t mtime;      time_t mtime;
# Line 68  struct uar_archive Line 69  struct uar_archive
69      bool is_stream;      bool is_stream;
70      uint8_t *buffer; /* Deprecated */      uint8_t *buffer; /* Deprecated */
71      FILE *stream;      FILE *stream;
72        uint64_t stream_size;
73      int last_errno;      int last_errno;
74      char *err_file;      char *err_file;
75      uar_create_callback_t create_callback;      uar_create_callback_t create_callback;
# Line 99  uar_strerror (const struct uar_archive * Line 101  uar_strerror (const struct uar_archive *
101              return "success";              return "success";
102          case UAR_INVALID_MAGIC:          case UAR_INVALID_MAGIC:
103              return "invalid archive magic";              return "invalid archive magic";
104            case UAR_INVALID_ARCHIVE:
105                return "invalid archive";
106            case UAR_UNSUPPORTED_VERSION:
107                return "archive version is not supported";
108          case UAR_INVALID_FILE:          case UAR_INVALID_FILE:
109              return "invalid file";              return "invalid file";
110          case UAR_INVALID_PATH:          case UAR_INVALID_PATH:
# Line 161  uar_create (void) Line 167  uar_create (void)
167      uar->files = NULL;      uar->files = NULL;
168      uar->stream = NULL;      uar->stream = NULL;
169      uar->root = NULL;      uar->root = NULL;
170        uar->stream_size = 0;
171      uar->last_errno = 0;      uar->last_errno = 0;
172      uar->err_file = NULL;      uar->err_file = NULL;
173    
# Line 181  uar_initialize_root (struct uar_archive Line 188  uar_initialize_root (struct uar_archive
188      root->type = UF_DIR;      root->type = UF_DIR;
189      root->mode = S_IFDIR | 0755;      root->mode = S_IFDIR | 0755;
190      root->mtime = time (NULL);      root->mtime = time (NULL);
191        root->uid = getuid ();
192        root->gid = getgid ();
193    
194      if (!uar_add_file_entry (uar, root))      if (!uar_add_file_entry (uar, root))
195          {          {
# Line 236  uar_stream_write (struct uar_archive *ua Line 245  uar_stream_write (struct uar_archive *ua
245                      fclose (stream);                      fclose (stream);
246                      return false;                      return false;
247                  }                  }
248    
249                if (file->type == UF_LINK)
250                    {
251                        if (fwrite (file->data.link.loc, 1, file->data.link.loclen,
252                                    stream)
253                            != file->data.link.loclen)
254                            {
255                                uar_set_error (uar, UAR_SYSCALL_ERROR, file->name);
256                                fclose (stream);
257                                return false;
258                            }
259                    }
260          }          }
261    
262      uar->stream = freopen (NULL, "rb", uar->stream);      uar->stream = freopen (NULL, "rb", uar->stream);
# Line 287  uar_stream_write (struct uar_archive *ua Line 308  uar_stream_write (struct uar_archive *ua
308                  size -= buf_size;                  size -= buf_size;
309          }          }
310    
311        free (buf);
312      fclose (stream);      fclose (stream);
313      return true;      return true;
314  }  }
315    
316  struct uar_archive *  struct uar_archive *
317  uar_create_stream (void)  uar_stream_create (void)
318  {  {
319      struct uar_archive *uar = uar_create ();      struct uar_archive *uar = uar_create ();
320      int cerrno;      int cerrno;
# Line 315  uar_create_stream_error: Line 337  uar_create_stream_error:
337      cerrno = errno;      cerrno = errno;
338      uar_close (uar);      uar_close (uar);
339      errno = cerrno;      errno = cerrno;
340        uar = NULL;
341  uar_create_stream_ret:  uar_create_stream_ret:
342      return uar;      return uar;
343  }  }
# Line 338  uar_stream_add_file (struct uar_archive Line 361  uar_stream_add_file (struct uar_archive
361      enum uar_error ecode = UAR_SUCCESS;      enum uar_error ecode = UAR_SUCCESS;
362      void *buffer = NULL;      void *buffer = NULL;
363      struct uar_file *file = NULL;      struct uar_file *file = NULL;
364        uint64_t uar_file_namelen = strlen (uar_filename);
365    
366        if (uar_file_namelen > PATH_MAX)
367            {
368                uar_set_error (uar, UAR_INVALID_PATH, fs_filename);
369                return NULL;
370            }
371    
372        bool contains_dot_dot
373            = (uar_file_namelen > 3 && uar_filename[0] == '.'
374               && uar_filename[1] == '.' && uar_filename[2] == '/');
375    
376        if ((uar_file_namelen > 2 && uar_filename[0] == '.'
377             && uar_filename[1] == '/')
378            || contains_dot_dot)
379            {
380                if (uar->create_callback != NULL)
381                    uar->create_callback (
382                        uar, NULL, uar_filename, fs_filename, UAR_ELEVEL_WARNING,
383                        contains_dot_dot ? "removing leading '..'"
384                                         : "removing leading '.'");
385    
386                uar_filename = uar_filename + 1 + (uar_file_namelen == 1);
387                uar_file_namelen -= 1 + (uar_file_namelen == 1);
388            }
389    
390      if (stinfo == NULL)      if (stinfo == NULL)
391          {          {
# Line 380  uar_stream_add_file (struct uar_archive Line 428  uar_stream_add_file (struct uar_archive
428    
429      fseek (stream, 0, SEEK_SET);      fseek (stream, 0, SEEK_SET);
430    
431      file = uar_file_create (uar_filename, 0, size, uar->header.size);      file = uar_file_create (uar_filename, uar_file_namelen, size,
432                                uar->header.size);
433    
434      if (file == NULL)      if (file == NULL)
435          {          {
# Line 513  uar_stream_add_dir (struct uar_archive * Line 562  uar_stream_add_dir (struct uar_archive *
562              struct uar_file *entry_file              struct uar_file *entry_file
563                  = uar_stream_add_entry (uar, uar_fullpath, fs_fullpath, NULL);                  = uar_stream_add_entry (uar, uar_fullpath, fs_fullpath, NULL);
564    
565              if (entry_file != NULL)              if (entry_file != NULL && entry_file->type != UF_LINK)
566                  size += entry_file->data.size;                  size += entry_file->data.size;
567    
568              free (fs_fullpath);              free (fs_fullpath);
# Line 565  uar_stream_add_link (struct uar_archive Line 614  uar_stream_add_link (struct uar_archive
614                  stinfo = &custom_stinfo;                  stinfo = &custom_stinfo;
615          }          }
616    
617        uint64_t uar_file_namelen = strlen (uar_name);
618    
619        bool contains_dot_dot = (uar_file_namelen > 3 && uar_name[0] == '.'
620                                 && uar_name[1] == '.' && uar_name[2] == '/');
621    
622        if ((uar_file_namelen > 2 && uar_name[0] == '.' && uar_name[1] == '/')
623            || contains_dot_dot)
624            {
625                if (uar->create_callback != NULL)
626                    uar->create_callback (
627                        uar, NULL, uar_name, fs_name, UAR_ELEVEL_WARNING,
628                        contains_dot_dot ? "removing leading '..'"
629                                         : "removing leading '.'");
630    
631                uar_name = uar_name + 1 + (uar_file_namelen == 1);
632                uar_file_namelen -= 1 + (uar_file_namelen == 1);
633            }
634    
635      file = uar_file_create (uar_name, 0, 0, uar->header.size);      file = uar_file_create (uar_name, 0, 0, uar->header.size);
636    
637      if (file == NULL)      if (file == NULL)
# Line 593  uar_stream_add_link (struct uar_archive Line 660  uar_stream_add_link (struct uar_archive
660              return NULL;              return NULL;
661          }          }
662    
663      file->data.linkinfo.loclen = link_len;      file->data.link.loclen = link_len;
664      file->data.linkinfo.loc = malloc (link_len + 1);      file->data.link.loc = malloc (link_len + 1);
665    
666      if (file->data.linkinfo.loc == NULL)      if (file->data.link.loc == NULL)
667          {          {
668              uar_set_error (uar, UAR_OUT_OF_MEMORY, fs_name);              uar_set_error (uar, UAR_OUT_OF_MEMORY, fs_name);
669              uar_file_destroy (file);              uar_file_destroy (file);
670              return NULL;              return NULL;
671          }          }
672    
673      memcpy (file->data.linkinfo.loc, link_buf, link_len);      memcpy (file->data.link.loc, link_buf, link_len);
674      file->data.linkinfo.loc[link_len] = 0;      file->data.link.loc[link_len] = 0;
675    
676      if (!uar_add_file_entry (uar, file))      if (!uar_add_file_entry (uar, file))
677          {          {
# Line 652  uar_stream_add_entry (struct uar_archive Line 719  uar_stream_add_entry (struct uar_archive
719                  }                  }
720          }          }
721      else if (S_ISDIR (stinfo->st_mode))      else if (S_ISDIR (stinfo->st_mode))
722          {          file = uar_stream_add_dir (uar, uar_name, fs_name, stinfo);
723              file = uar_stream_add_dir (uar, uar_name, fs_name, stinfo);  
724          }      else if (S_ISLNK (stinfo->st_mode))
725            file = uar_stream_add_link (uar, uar_name, fs_name, stinfo);
726      else      else
727          {          {
728              file = uar_stream_add_link (uar, uar_name, fs_name, stinfo);              uar_set_error (uar, UAR_INVALID_FILE, fs_name);
729                return NULL;
730          }          }
731    
732      if (file != NULL)      if (file != NULL)
# Line 671  uar_stream_add_entry (struct uar_archive Line 740  uar_stream_add_entry (struct uar_archive
740      return file;      return file;
741  }  }
742    
743    /* Validate the UAR archive header. */
744    bool
745    uar_stream_header_validate (struct uar_archive *uar)
746    {
747        /* Compare magic to ensure it's a valid UAR archive. */
748        if (memcmp (uar->header.magic, UAR_MAGIC, sizeof (UAR_MAGIC)) != 0)
749            {
750                uar_set_error (uar, UAR_INVALID_MAGIC, NULL);
751                return false;
752            }
753    
754        /* Check if the version is supported. */
755        if (uar->header.version > UAR_MAX_SUPPORTED_VERSION)
756            {
757                uar_set_error (uar, UAR_UNSUPPORTED_VERSION, NULL);
758                return false;
759            }
760    
761        /* Check if the data block size is valid, to prevent buffer overflow. If
762           it's larger than the stream  size, it's invalid. This could be because
763           the archive is corrupted, or it's a malicious archive. */
764        if (uar->header.size > (uar->stream_size - sizeof (struct uar_header)))
765            {
766                uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
767                return false;
768            }
769    
770        /* At the moment, UAR doesn't support any flags. */
771        if (uar->header.flags != 0)
772            {
773                uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
774                return false;
775            }
776    
777        /* Check if the file is big enough to hold n number of files. */
778        if (uar->header.nfiles * sizeof (struct uar_file) > uar->header.size)
779            {
780                uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
781                return false;
782            }
783    
784        return true;
785    }
786    
787    struct uar_archive *
788    uar_stream_open (const char *filename)
789    {
790        struct uar_archive *uar;
791        FILE *stream = fopen (filename, "rb");
792    
793        if (stream == NULL)
794            return NULL;
795    
796        uar = uar_create ();
797    
798        if (uar == NULL)
799            return NULL;
800    
801        uar->is_stream = true;
802        uar->stream = stream;
803    
804        fseek (stream, 0, SEEK_END);
805        long size = ftell (stream);
806    
807        if (size < 0 || size > INT64_MAX)
808            {
809                uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
810                return uar;
811            }
812    
813        fseek (stream, 0, SEEK_SET);
814    
815        if (((size_t) size) < sizeof (struct uar_header))
816            {
817                uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
818                return uar;
819            }
820    
821        if (fread (&uar->header, 1, sizeof (struct uar_header), stream)
822            != sizeof (struct uar_header))
823            {
824                uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
825                return uar;
826            }
827    
828        uar->stream_size = size;
829    
830        if (!uar_stream_header_validate (uar))
831            return uar;
832    
833        uar->files = calloc (uar->header.nfiles, sizeof (struct uar_file *));
834    
835        if (uar->files == NULL)
836            {
837                uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
838                return uar;
839            }
840    
841        uint64_t read_size = sizeof (struct uar_header)
842                             + (uar->header.nfiles * sizeof (struct uar_file));
843    
844        for (uint64_t i = 0; i < uar->header.nfiles; i++)
845            {
846                struct uar_file *file = malloc (sizeof (struct uar_file));
847    
848                if (file == NULL)
849                    {
850                        uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
851                        return uar;
852                    }
853    
854                if (fread (file, 1, sizeof (struct uar_file), stream)
855                    != sizeof (struct uar_file))
856                    {
857                        uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
858                        free (file);
859                        return uar;
860                    }
861    
862                /* Right after the file structure, the name of the file is stored,
863                   with the length of the name stored in the namelen field.
864                   First, we need to check if the namelen is valid.
865                 */
866    
867                if (file->namelen > PATH_MAX || file->namelen == 0
868                    || read_size + file->namelen > ((uint64_t) size))
869                    {
870                        /* At a later stage, we might want to rather call a callback
871                           function instead. */
872                        uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
873                        free (file);
874                        return uar;
875                    }
876    
877                file->name = malloc (file->namelen + 1);
878    
879                if (file->name == NULL)
880                    {
881                        uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
882                        free (file);
883                        return uar;
884                    }
885    
886                if (fread (file->name, 1, file->namelen, stream) != file->namelen)
887                    {
888                        uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
889                        free (file->name);
890                        free (file);
891                        return uar;
892                    }
893    
894                file->name[file->namelen] = 0;
895    
896                /* Next, we need to check if the file is a link. If it is, we need
897                   to read the link location. */
898    
899                if (file->type == UF_LINK)
900                    {
901                        if (file->data.link.loclen > PATH_MAX
902                            || read_size + file->data.link.loclen
903                                   > ((uint64_t) size))
904                            {
905                                uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL);
906                                free (file->name);
907                                free (file);
908                                return uar;
909                            }
910    
911                        file->data.link.loc = malloc (file->data.link.loclen + 1);
912    
913                        if (file->data.link.loc == NULL)
914                            {
915                                uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL);
916                                free (file->name);
917                                free (file);
918                                return uar;
919                            }
920    
921                        if (fread (file->data.link.loc, 1, file->data.link.loclen,
922                                   stream)
923                            != file->data.link.loclen)
924                            {
925                                uar_set_error (uar, UAR_SYSCALL_ERROR, NULL);
926                                free (file->name);
927                                free (file->data.link.loc);
928                                free (file);
929                                return uar;
930                            }
931    
932                        file->data.link.loc[file->data.link.loclen] = 0;
933                    }
934    
935                uar->files[i] = file;
936            }
937    
938        return uar;
939    }
940    
941  struct uar_archive *  struct uar_archive *
942  uar_open (const char *filename)  uar_open (const char *filename)
943  {  {
# Line 798  uar_file_destroy (struct uar_file *file) Line 1065  uar_file_destroy (struct uar_file *file)
1065          return;          return;
1066    
1067      if (file->type == UF_LINK)      if (file->type == UF_LINK)
1068          free (file->data.linkinfo.loc);          free (file->data.link.loc);
1069    
1070      free (file->name);      free (file->name);
1071      free (file);      free (file);
# Line 1203  uar_debug_print (const struct uar_archiv Line 1470  uar_debug_print (const struct uar_archiv
1470              printf ("    mode: %04o\n", file->mode);              printf ("    mode: %04o\n", file->mode);
1471    
1472              if (file->type == UF_LINK)              if (file->type == UF_LINK)
1473                  printf ("    points to: %s\n", file->data.linkinfo.loc);                  printf ("    points to: %s\n", file->data.link.loc);
1474              else              else
1475                  printf ("    size: %lu\n", file->data.size);                  printf ("    size: %lu\n", file->data.size);
1476    
# Line 1392  const char * Line 1659  const char *
1659  uar_get_error_file (const struct uar_archive *uar)  uar_get_error_file (const struct uar_archive *uar)
1660  {  {
1661      return uar->err_file;      return uar->err_file;
1662    }
1663    
1664    uid_t
1665    uar_file_get_uid (const struct uar_file *file)
1666    {
1667        return file->uid;
1668    }
1669    
1670    gid_t
1671    uar_file_get_gid (const struct uar_file *file)
1672    {
1673        return file->gid;
1674  }  }

Legend:
Removed from v.35  
changed lines
  Added in v.36

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26