root/lib-src/asset-directory-tool.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. croak
  2. xmalloc
  3. main_1
  4. main_2
  5. main

     1 /* Android asset directory tool.
     2 
     3 Copyright (C) 2023 Free Software Foundation, Inc.
     4 
     5 This file is part of GNU Emacs.
     6 
     7 GNU Emacs is free software: you can redistribute it and/or modify
     8 it under the terms of the GNU General Public License as published by
     9 the Free Software Foundation, either version 3 of the License, or (at
    10 your option) any later version.
    11 
    12 GNU Emacs is distributed in the hope that it will be useful,
    13 but WITHOUT ANY WARRANTY; without even the implied warranty of
    14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15 GNU General Public License for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    19 
    20 #include <config.h>
    21 
    22 #include <stdio.h>
    23 #include <fcntl.h>
    24 #include <errno.h>
    25 #include <byteswap.h>
    26 #include <stdlib.h>
    27 #include <dirent.h>
    28 #include <string.h>
    29 #include <unistd.h>
    30 
    31 #include <sys/stat.h>
    32 
    33 /* This program takes a directory as input, and generates a
    34    ``directory-tree'' file suitable for inclusion in an Android
    35    application package.
    36 
    37    Such a file records the layout of the `assets' directory in the
    38    package.  Emacs records this information itself and uses it in the
    39    Android emulation of readdir, because the system asset manager APIs
    40    are routinely buggy, and are often unable to locate directories or
    41    files.
    42 
    43    The file is packed, with no data alignment guarantees made.  The
    44    file starts with the bytes "EMACS", following which is the name of
    45    the first file or directory, a NULL byte and an unsigned int
    46    indicating the offset from the start of the file to the start of
    47    the next sibling.  Following that is a list of subdirectories or
    48    files in the same format.  The long is stored LSB first.
    49 
    50    Directories can be distinguished from ordinary files through the
    51    last bytes of their file names (immediately previous to their
    52    terminating NULL bytes), which are set to the directory separator
    53    character `/'.  */
    54 
    55 
    56 
    57 struct directory_tree
    58 {
    59   /* The offset to the next sibling.  */
    60   size_t offset;
    61 
    62   /* The name of this directory or file.  */
    63   char *name;
    64 
    65   /* Subdirectories and files inside this directory.  */
    66   struct directory_tree *children, *next;
    67 };
    68 
    69 /* Exit with EXIT_FAILURE, after printing a description of a failing
    70    function WHAT along with the details of the error.  */
    71 
    72 static _Noreturn void
    73 croak (const char *what)
    74 {
    75   perror (what);
    76   exit (EXIT_FAILURE);
    77 }
    78 
    79 /* Like malloc, but aborts on failure.  */
    80 
    81 static void *
    82 xmalloc (size_t size)
    83 {
    84   void *ptr;
    85 
    86   ptr = malloc (size);
    87 
    88   if (!ptr)
    89     croak ("malloc");
    90 
    91   return ptr;
    92 }
    93 
    94 /* Recursively build a struct directory_tree structure for each
    95    subdirectory or file in DIR, in preparation for writing it out to
    96    disk.  PARENT should be the directory tree associated with the
    97    parent directory, or else PARENT->offset must be initialized to
    98    5.  */
    99 
   100 static void
   101 main_1 (DIR *dir, struct directory_tree *parent)
   102 {
   103   struct dirent *dirent;
   104   int dir_fd, fd;
   105   struct stat statb;
   106   struct directory_tree *this, **last;
   107   size_t length;
   108   DIR *otherdir;
   109 
   110   dir_fd = dirfd (dir);
   111   last = &parent->children;
   112 
   113   while ((dirent = readdir (dir)))
   114     {
   115       /* Determine what kind of file DIRENT is.  */
   116 
   117       if (fstatat (dir_fd, dirent->d_name, &statb,
   118                    AT_SYMLINK_NOFOLLOW) == -1)
   119         croak ("fstatat");
   120 
   121       /* Ignore . and ...  */
   122 
   123       if (!strcmp (dirent->d_name, ".")
   124           || !strcmp (dirent->d_name, ".."))
   125         continue;
   126 
   127       length = strlen (dirent->d_name);
   128 
   129       if (statb.st_mode & S_IFDIR)
   130         {
   131           /* This is a directory.  Write its name followed by a
   132              trailing slash, then a NULL byte, and the offset to the
   133              next sibling.  */
   134           this = xmalloc (sizeof *this);
   135           this->children = NULL;
   136           this->next = NULL;
   137           *last = this;
   138           last = &this->next;
   139           this->name = xmalloc (length + 2);
   140           strcpy (this->name, dirent->d_name);
   141 
   142           /* Now record the offset to the end of this directory.  This
   143              is length + 1, for the file name, and 5 more bytes for
   144              the trailing NULL and long.  */
   145           this->offset = parent->offset + length + 6;
   146 
   147           /* Terminate that with a slash and trailing NULL byte.  */
   148           this->name[length] = '/';
   149           this->name[length + 1] = '\0';
   150 
   151           /* Open and build that directory recursively.  */
   152 
   153           fd = openat (dir_fd, dirent->d_name, O_DIRECTORY,
   154                        O_RDONLY);
   155           if (fd < 0)
   156             croak ("openat");
   157           otherdir = fdopendir (fd);
   158           if (!otherdir)
   159             croak ("fdopendir");
   160 
   161           main_1 (otherdir, this);
   162 
   163           /* Close this directory.  */
   164           closedir (otherdir);
   165 
   166           /* Finally, set parent->offset to this->offset as well.  */
   167           parent->offset = this->offset;
   168         }
   169       else if (statb.st_mode & S_IFREG)
   170         {
   171           /* This is a regular file.  */
   172           this = xmalloc (sizeof *this);
   173           this->children = NULL;
   174           this->next = NULL;
   175           *last = this;
   176           last = &this->next;
   177           this->name = xmalloc (length + 1);
   178           strcpy (this->name, dirent->d_name);
   179 
   180           /* This is one byte shorter because there is no trailing
   181              slash.  */
   182           this->offset = parent->offset + length + 5;
   183           parent->offset = this->offset;
   184         }
   185     }
   186 }
   187 
   188 /* Write the struct directory_tree TREE and all of is children to the
   189    file descriptor FD.  OFFSET is the offset of TREE and may be
   190    modified; it is only used for checking purposes.  */
   191 
   192 static void
   193 main_2 (int fd, struct directory_tree *tree, size_t *offset)
   194 {
   195   ssize_t size;
   196   struct directory_tree *child;
   197   unsigned int output;
   198 
   199   /* Write tree->name with the trailing NULL byte.  */
   200   size = strlen (tree->name) + 1;
   201   if (write (fd, tree->name, size) < size)
   202     croak ("write");
   203 
   204   /* Write the offset.  */
   205 #ifdef WORDS_BIGENDIAN
   206   output = bswap_32 (tree->offset);
   207 #else
   208   output = tree->offset;
   209 #endif
   210   if (write (fd, &output, 4) < 1)
   211     croak ("write");
   212   size += 4;
   213 
   214   /* Now update offset.  */
   215   *offset += size;
   216 
   217   /* Write out each child.  */
   218   for (child = tree->children; child; child = child->next)
   219     main_2 (fd, child, offset);
   220 
   221   /* Verify the offset is correct.  */
   222   if (tree->offset != *offset)
   223     {
   224       fprintf (stderr,
   225                "asset-directory-tool: invalid offset: expected %tu, "
   226                "got %tu.\n"
   227                "Please report this bug to bug-gnu-emacs@gnu.org, along\n"
   228                "with an archive containing the contents of the java/inst"
   229                "all_temp directory.\n",
   230                tree->offset, *offset);
   231       abort ();
   232     }
   233 }
   234 
   235 int
   236 main (int argc, char **argv)
   237 {
   238   int fd;
   239   DIR *indir;
   240   struct directory_tree tree;
   241   size_t offset;
   242 
   243   if (argc != 3)
   244     {
   245       fprintf (stderr, "usage: %s directory output-file\n",
   246                argv[0]);
   247       return EXIT_FAILURE;
   248     }
   249 
   250   fd = open (argv[2], O_CREAT | O_TRUNC | O_RDWR,
   251              S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
   252 
   253   if (fd < 0)
   254     {
   255       perror ("open");
   256       return EXIT_FAILURE;
   257     }
   258 
   259   indir = opendir (argv[1]);
   260 
   261   if (!indir)
   262     {
   263       perror ("opendir");
   264       return EXIT_FAILURE;
   265     }
   266 
   267   /* Write the first 5 byte header to FD.  */
   268 
   269   if (write (fd, "EMACS", 5) < 5)
   270     {
   271       perror ("write");
   272       return EXIT_FAILURE;
   273     }
   274 
   275   /* Now iterate through children of INDIR, building the directory
   276      tree.  */
   277   tree.offset = 5;
   278   tree.children = NULL;
   279 
   280   main_1 (indir, &tree);
   281   closedir (indir);
   282 
   283   /* Finally, write the directory tree to the output file.  */
   284   offset = 5;
   285   for (; tree.children; tree.children = tree.children->next)
   286     main_2 (fd, tree.children, &offset);
   287 
   288   return 0;
   289 }

/* [<][>][^][v][top][bottom][index][help] */