L4Re/departure

libe2access/host/e2access.c

250:5c301fd60418
2022-02-16 Paul Boddie Moved common directory listing functionality into a new utilities collection.
     1 /*     2  * Access a filesystem.     3  *     4  * Copyright (C) 2019, 2022 Paul Boddie <paul@boddie.org.uk>     5  *     6  * This program is free software; you can redistribute it and/or     7  * modify it under the terms of the GNU General Public License as     8  * published by the Free Software Foundation; either version 2 of     9  * the License, or (at your option) any later version.    10  *    11  * This program is distributed in the hope that it will be useful,    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    14  * GNU General Public License for more details.    15  *    16  * You should have received a copy of the GNU General Public License    17  * along with this program; if not, write to the Free Software    18  * Foundation, Inc., 51 Franklin Street, Fifth Floor,    19  * Boston, MA  02110-1301, USA    20  */    21     22 #include <stdio.h>    23 #include <stdlib.h>    24 #include <string.h>    25 #include <unistd.h>    26     27 #include <sys/types.h>    28 #include <sys/stat.h>    29     30 #include <ext2fs/ext2fs.h>    31     32 #include "file.h"    33 #include "format.h"    34 #include "image.h"    35 #include "path.h"    36 #include "utils.h"    37     38     39     40 /* Copy buffer size. */    41     42 const int BUFSIZE = 4096;    43     44     45     46 /* Alternative metadata set by options. */    47     48 struct metadata    49 {    50     uid_t uid;    51     gid_t gid;    52     mode_t mask;    53     int have_uid, have_gid;    54 };    55     56 struct metadata md;    57     58 /* Parse program options. */    59     60 static int parse_options(int argc, char *argv[])    61 {    62     int opt;    63     64     md.have_uid = 0;    65     md.have_gid = 0;    66     md.mask = 0000;    67     68     while ((opt = getopt(argc, argv, "g:m:u:")) != -1)    69     {    70         switch (opt)    71         {    72             case 'g':    73             md.gid = atoi(optarg);    74             md.have_gid = 1;    75             break;    76     77             case 'm':    78             md.mask = strtol(optarg, NULL, 0);    79             break;    80     81             case 'u':    82             md.uid = atoi(optarg);    83             md.have_uid = 1;    84             break;    85     86             default:    87             fprintf(stderr, "Option not recognised: %s\n", argv[optind]);    88             return -1;    89         }    90     }    91     92     return 0;    93 }    94     95     96     97 /* Copy a file into the filesystem image. */    98     99 int copy_file_in(const char *filename, ext2_filsys fs, ext2_ino_t ino_file, int flags)   100 {   101     int retval = 0;   102     ext2_file_t file;   103    104     /* Copying details. */   105    106     FILE *fp;   107     char buf[BUFSIZE];   108     size_t got;   109     unsigned int written;   110    111     /* Open a file in the target directory. */   112    113     if (ext2fs_file_open(fs, ino_file, flags, &file))   114         return 1;   115    116     /* Open the file in the source directory. */   117    118     fp = fopen(filename, "r");   119    120     /* Copy the file content. */   121    122     if (fp != NULL)   123     {   124         while (got = fread(buf, sizeof(char), BUFSIZE, fp))   125         {   126             while (got)   127             {   128                 if (ext2fs_file_write(file, buf, got, &written))   129                 {   130                     retval = 1;   131                     goto close_files;   132                 }   133                 got -= written;   134             }   135         }   136     }   137    138 close_files:   139     fclose(fp);   140     ext2fs_file_flush(file);   141     ext2fs_file_close(file);   142    143     return retval;   144 }   145    146 /* Copy a file out of the filesystem image. */   147    148 int copy_file_out(const char *target, const char *filename, int target_is_file,   149                   ext2_filsys fs, ext2_ino_t ino_file)   150 {   151     int retval = 0;   152     ext2_file_t file;   153    154     /* Copying details. */   155    156     FILE *fp;   157     char buf[BUFSIZE];   158     unsigned int got;   159     size_t written;   160    161     /* Open the file in the source directory. */   162    163     if (ext2fs_file_open(fs, ino_file, 0, &file))   164         return 1;   165    166     /* Open a file in the target directory. */   167    168     if (target_is_file)   169         fp = fopen(target, "w");   170     else   171         fp = open_file_in_dir(target, path_basename(filename), "w");   172    173     /* Copy the file content. */   174    175     if (fp != NULL)   176     {   177         do   178         {   179             if (ext2fs_file_read(file, buf, BUFSIZE, &got))   180             {   181                 retval = 1;   182                 goto close_files;   183             }   184    185             while (got)   186             {   187                 written = fwrite(buf, sizeof(char), got, fp);   188                 got -= written;   189             }   190    191         } while (got);   192     }   193    194 close_files:   195     fclose(fp);   196     ext2fs_file_close(file);   197    198     return retval;   199 }   200    201    202    203 /* Copy source files from the external environment into the filesystem image. */   204    205 int copy_in(ext2_filsys fs, int argc, char *argv[])   206 {   207     errcode_t retval;   208    209     /* Target filename details. */   210    211     const char *target = argv[argc - 1];   212     const char *target_remaining = argv[argc - 1];   213     const char *basename;   214     int target_is_file;   215    216     /* Target file and directory details. */   217    218     int target_is_new;   219     ext2_ino_t ino_file, ino_target;   220     int flags;   221    222     /* Source file details. */   223    224     struct stat st;   225     int i;   226    227     /* Locate the target and test whether it is a file or a directory. */   228    229     if (image_find_path(fs, &target_remaining, &ino_target))   230     {   231         /* Only a non-existent file in an existing directory is permitted. */   232    233         if (!_image_isdir(fs, ino_target) || !path_is_leafname(target_remaining))   234         {   235             fprintf(stderr, "Target not found: %s\n", target);   236             return 1;   237         }   238    239         target_is_file = 1;   240         target_is_new = 1;   241     }   242     else   243     {   244         target_is_file = _image_isfile(fs, ino_target);   245         target_is_new = 0;   246     }   247    248     /* Only permit a target file when one source file is given. */   249    250     if (target_is_file)   251     {   252         if (argc > 2)   253         {   254             fprintf(stderr, "Target can only be a file when copying a single file: %s\n", target);   255             return 1;   256         }   257     }   258     else if (!_image_isdir(fs, ino_target))   259     {   260         fprintf(stderr, "Target is not a directory: %s\n", target);   261         return 1;   262     }   263    264     /* Copy each source object to the target directory. */   265    266     for (i = 0; i < argc - 1; i++)   267     {   268         if (target_is_file)   269             basename = target_remaining;   270         else   271         {   272             basename = path_basename(argv[i]);   273             target_is_new = image_find_file(fs, target, basename, &ino_file);   274         }   275    276         /* Directories are created with the same metadata. */   277    278         if (isdir(argv[i]))   279         {   280             if (!target_is_new)   281             {   282                 fprintf(stderr, "Target cannot be created since it already exists: %s\n", target);   283                 return 1;   284             }   285    286             /* Obtain the metadata. */   287    288             if (lstat(argv[i], &st))   289             {   290                 fprintf(stderr, "Failed to read object metadata: %s\n", argv[i]);   291                 return 1;   292             }   293    294             retval = image_make_dir(fs, ino_target, basename,   295                                     st.st_mode & ~md.mask,   296                                     md.have_uid ? md.uid : st.st_uid,   297                                     md.have_gid ? md.gid : st.st_gid,   298                                     &ino_file);   299    300             if (retval)   301             {   302                 fprintf(stderr, "Failed to create directory: %s\n", argv[i]);   303                 return 1;   304             }   305         }   306    307         /* Files are copied. */   308    309         else if (isfile(argv[i]))   310         {   311             flags = EXT2_FILE_WRITE;   312    313             /* Obtain the inode for the target file. */   314    315             if (target_is_new)   316             {   317                 /* Populate the inode details. */   318    319                 if (lstat(argv[i], &st))   320                 {   321                     fprintf(stderr, "Failed to read object metadata: %s\n", argv[i]);   322                     return 1;   323                 }   324    325                 retval = image_create_file(fs, ino_target, basename,   326                                            st.st_mode & ~md.mask,   327                                            md.have_uid ? md.uid : st.st_uid,   328                                            md.have_gid ? md.gid : st.st_gid,   329                                            &ino_file);   330                 if (retval)   331                 {   332                     fprintf(stderr, "Failed to create file: %s\n", argv[i]);   333                     return 1;   334                 }   335    336                 flags |= EXT2_FILE_CREATE;   337             }   338    339             /* NOTE: Overwrite/update metadata where appropriate. */   340    341             if (copy_file_in(argv[i], fs, ino_file, flags))   342             {   343                 fprintf(stderr, "Failed to write to file: %s\n", argv[i]);   344                 return 1;   345             }   346         }   347     }   348    349     return 0;   350 }   351    352 /* Copy source files out of the filesystem image into the external environment. */   353    354 int copy_out(ext2_filsys fs, int argc, char *argv[])   355 {   356     /* Target filename details. */   357    358     char *target = argv[argc - 1];   359     int target_is_file;   360    361     /* Target file and directory details. */   362    363     ext2_file_t file;   364     ext2_ino_t ino_file;   365    366     /* Source file details. */   367    368     const char *path;   369     int i;   370    371     /* Locate the target and test whether it is a directory. */   372    373     if (!isdir(target))   374     {   375         /* Only a new or existing file in an existing directory is permitted. */   376    377         if (isfile(target) || isdir_dirname(target))   378             target_is_file = 1;   379         else   380         {   381             fprintf(stderr, "Target is not a directory: %s\n", target);   382             return 1;   383         }   384    385         /* Only permit a target file when one source file is given. */   386    387         if (argc > 2)   388         {   389             fprintf(stderr, "Target can only be a file when copying a single file: %s\n", target);   390             return 1;   391         }   392     }   393     else   394         target_is_file = 0;   395    396     /* For each source filename, test whether it references a file. */   397    398     for (i = 0; i < argc - 1; i++)   399     {   400         if (!image_isfile(fs, argv[i]))   401         {   402             fprintf(stderr, "Source is not a file: %s\n", argv[i]);   403             return 1;   404         }   405     }   406    407     /* Copy each source file to the target directory. */   408    409     for (i = 0; i < argc - 1; i++)   410     {   411         path = argv[i];   412    413         if (image_find_path(fs, &path, &ino_file))   414         {   415             fprintf(stderr, "Failed to find file: %s\n", argv[i]);   416             return 1;   417         }   418    419         if (copy_file_out(target, argv[i], target_is_file, fs, ino_file))   420         {   421             fprintf(stderr, "Failed to read from file: %s\n", argv[i]);   422             return 1;   423         }   424    425         /* NOTE: Overwrite/update metadata where appropriate. */   426     }   427    428     return 0;   429 }   430    431 /* List objects in the filesystem image. */   432    433 int list(ext2_filsys fs, int argc, char *argv[])   434 {   435     int i;   436     char *path;   437    438     for (i = 0; i < argc; i++)   439     {   440         path = argv[i];   441    442         /* Emit each object. */   443    444         puts(path);   445    446         /* List individual files or directories. */   447    448         if (utils_list_dir(fs, path))   449         {   450             fprintf(stderr, "Failed to list directory: %s\n", path);   451             return 1;   452         }   453     }   454    455     return 0;   456 }   457    458 /* Make directories in the filesystem image. */   459    460 int make_dirs(ext2_filsys fs, int argc, char *argv[])   461 {   462     int i;   463     const char *path;   464     ext2_ino_t ino;   465    466     /* Make each directory component in the given pathname. */   467    468     for (i = 0; i < argc; i++)   469     {   470         path = argv[i];   471    472         /* Search for the remaining components. */   473    474         if ((!*path) || !image_find_path(fs, &path, &ino))   475         {   476             fprintf(stderr, "Path exists: %s\n", argv[i]);   477             return 1;   478         }   479    480         /* From the first unrecognised component, make the remaining   481            directories. */   482    483         if (image_make_dirs(fs, &path, ino,   484                             0777 & ~md.mask,   485                             md.have_uid ? md.uid : 0,   486                             md.have_gid ? md.gid : 0))   487         {   488             fprintf(stderr, "Failed to make directory: %s\n", argv[i]);   489             return 1;   490         }   491     }   492    493     return 0;   494 }   495    496 /* Remove directories from the filesystem image. */   497    498 int remove_dirs(ext2_filsys fs, int argc, char *argv[])   499 {   500     int i;   501     const char *path;   502     ext2_ino_t ino;   503    504     /* Remove each directory with the given pathname. */   505    506     for (i = 0; i < argc; i++)   507     {   508         path = argv[i];   509    510         /* Detect missing objects. */   511    512         if ((!*path) || !image_exists(fs, path))   513         {   514             fprintf(stderr, "Not found: %s\n", path);   515             return 1;   516         }   517    518         /* Insist on a directory. */   519    520         if (!image_isdir(fs, path))   521         {   522             fprintf(stderr, "Not a directory: %s\n", path);   523             return 1;   524         }   525    526         /* Test for an empty directory. */   527    528         if (image_dir_empty_by_path(fs, path, &ino))   529         {   530             fprintf(stderr, "Directory not empty: %s\n", path);   531             return 1;   532         }   533    534         /* Unlink the directory. */   535    536         if (image_unlink_by_path(fs, path))   537         {   538             fprintf(stderr, "Could not unlink directory: %s\n", path);   539             return 1;   540         }   541    542         /* Remove the directory. */   543    544         if (image_remove_by_inode(fs, ino))   545         {   546             fprintf(stderr, "Could not remove directory: %s\n", path);   547             return 1;   548         }   549     }   550    551     return 0;   552 }   553    554    555    556 /* Help message. */   557    558 char help_text[] = "\   559 Usage: %s [ <options> ] <image file> <operation> <filename>...\n\   560 \n\   561 File ownership options:\n\   562 \n\   563   -g GID        Set group identifier for new files\n\   564   -u UID        Set user identifier for new files\n\   565 \n\   566 File permission options:\n\   567 \n\   568   -m MASK       Set mode/permissions mask for new directories\n\   569 \n\   570 Operations:\n\   571 \n\   572   copy-in       Copy files into a directory within the image\n\   573   copy-out      Copy files from the image into a directory\n\   574   ls            List files and directories within the image\n\   575   mkdir         Make directories within the image\n\   576   rmdir         Remove directories from the image\n\   577 ";   578    579 /* Operations exposed by the program. */   580    581 struct operation   582 {   583     const char *name;   584     int (*fn)(ext2_filsys, int, char *[]);   585 };   586    587 static struct operation operations[] = {   588     {"copy-in", copy_in},   589     {"copy-out", copy_out},   590     {"ls", list},   591     {"mkdir", make_dirs},   592     {"rmdir", remove_dirs},   593     {NULL, NULL},   594     };   595    596 /* Main program. */   597    598 int main(int argc, char *argv[])   599 {   600     int flags = EXT2_FLAG_RW; // | EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS;   601     ext2_filsys fs = NULL;   602     errcode_t retval;   603     int exitcode = 0;   604    605     /* Program argument details. */   606    607     char **args;   608     char *fsname, *operation, *filename;   609     int num_args;   610     struct operation *op;   611    612     /* Parse program options and initialise the argument details. */   613    614     if (parse_options(argc, argv))   615         return 1;   616    617     args = &argv[optind];   618     num_args = argc - optind;   619    620     if (num_args < 3)   621     {   622         fprintf(stderr, help_text, argv[0]);   623         return 1;   624     }   625    626     /* Open the filesystem image using the POSIX file access mechanism. */   627    628     fsname = args[0];   629    630     retval = ext2fs_open(fsname, flags, 0, 0, unix_io_manager, &fs);   631     if (retval)   632     {   633         fprintf(stderr, "Could not open filesystem: %s\n", fsname);   634         return 1;   635     }   636    637     //fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;   638    639     retval = ext2fs_read_bitmaps(fs);   640     if (retval)   641     {   642         fprintf(stderr, "Could not read bitmaps: %s\n", fsname);   643         return 1;   644     }   645    646     /* Perform the requested operation. */   647    648     operation = args[1];   649     args = &args[2];   650     num_args -= 2;   651    652     for (op = &operations[0]; op->name != NULL; op++)   653     {   654         if (!strcmp(operation, op->name))   655         {   656             exitcode = op->fn(fs, num_args, args);   657             if (exitcode)   658                 fprintf(stderr, "Operation failed: %s\n", operation);   659             break;   660         }   661     }   662    663     if (op->name == NULL)   664     {   665         fprintf(stderr, "Operation not recognised: %s\n", operation);   666         exitcode = 1;   667     }   668    669     /* Close the filesystem image. */   670    671     retval = ext2fs_flush(fs);   672     if (retval)   673     {   674         fprintf(stderr, "Error flushing filesystem: %s\n", fsname);   675         exitcode = 1;   676     }   677    678     retval = ext2fs_close(fs);   679     if (retval)   680     {   681         fprintf(stderr, "Error closing filesystem: %s\n", fsname);   682         exitcode = 1;   683     }   684    685     ext2fs_free(fs);   686     return exitcode;   687 }   688    689 /* vim: tabstop=4 expandtab shiftwidth=4   690 */