Changeset d84e346 in rtems


Ignore:
Timestamp:
May 18, 2016, 11:18:21 PM (3 years ago)
Author:
Chris Johns <chrisj@…>
Branches:
master
Children:
89f8d9fc
Parents:
560db810
git-author:
Chris Johns <chrisj@…> (05/18/16 23:18:21)
git-committer:
Chris Johns <chrisj@…> (06/03/16 08:14:20)
Message:

libmisc/untar: Support directory create and overwrites. Share the common code.

Support creating directories for files with a path depth greater than 1. Some
tar files can have files with a path depth greater than 1 and no directory
entry in the tar file to create a directory.

Support overwriting existing files and directories failing in a similar
way to tar on common hosts. If a file is replaced with a file delete the
file and create a new file. If a directory replaces a file remove the file
and create the directory. If a file replaces a directory remove the directory,
and if the directory is not empty and cannot be removed report an error. If a
directory alreday exists do nothing leaving the contents untouched.

The untar code now shares the common header parsing and initial processing
with the actual writes still separate. No changes to the IMFS have been made.

Updates #2415.
Closes #2207.

Files:
3 edited

Legend:

Unmodified
Added
Removed
  • cpukit/libmisc/untar/untar.c

    r560db810 rd84e346  
    1414/*
    1515 *  Written by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
    16 
     16 *
     17 *  Copyright 2016 Chris Johns <chrisj@rtems.org>
     18 *
    1719 *  The license and distribution terms for this file may be
    1820 *  found in the file LICENSE in this distribution or at
     
    2426#endif
    2527
     28#include <stdbool.h>
    2629#include <sys/param.h>
    2730#include <stdio.h>
     
    6568 *   for(i = 0; i < 512; i++)
    6669 *       sum += 0xFF & header[i];
    67  */ 
     70 */
    6871
    6972#define MAX_NAME_FIELD_SIZE      99
     
    7275 * This converts octal ASCII number representations into an
    7376 * unsigned long.  Only support 32-bit numbers for now.
    74  */
     77 *
     78 * warning: this code is referenced in the IMFS.
     79 */
    7580unsigned long
    7681_rtems_octal2ulong(
     
    9398
    9499/*
     100 * Common error message formatter.
     101 */
     102static void
     103Print_Error(const rtems_printer *printer, const char* message, const char* path)
     104{
     105  rtems_printf(printer, "untar: %s: %s: (%d) %s\n",
     106               message, path, errno, strerror(errno));
     107}
     108
     109/*
     110 * Get the type of node on in the file system if present.
     111 */
     112static int
     113Stat_Node(const char* path)
     114{
     115  struct stat sb;
     116  if (stat(path, &sb) < 0)
     117    return -1;
     118  if (S_ISDIR(sb.st_mode))
     119    return DIRTYPE;
     120  return REGTYPE;
     121}
     122
     123/*
     124 * Make the directory path for a file if it does not exist.
     125 */
     126static int
     127Make_Path(const rtems_printer *printer, const char* filename, bool end_is_dir)
     128{
     129  char* copy = strdup(filename);
     130  char* path = copy;
     131
     132  /*
     133   * Skip leading path separators.
     134   */
     135  while (*path == '/')
     136    ++path;
     137
     138  /*
     139   * Any path left?
     140   */
     141  if (*path != '\0') {
     142    bool  path_end = false;
     143    char* end = path;
     144    int   r;
     145
     146    /*
     147     * Split the path into directory components. Check the node and if a file
     148     * and not the end of the path remove it and create a directory. If a
     149     * directory and not the end of the path decend into the directory.
     150     */
     151    while (!path_end) {
     152      while (*end != '\0' && *end != '/')
     153        ++end;
     154
     155      /*
     156       * Are we at the end of the path?
     157       */
     158      if (*end == '\0')
     159        path_end = true;
     160
     161      /*
     162       * Split the path.
     163       */
     164      *end = '\0';
     165
     166      /*
     167       * Get the node's status, exists, error, directory or regular? Regular
     168       * means not a directory.
     169       */
     170      r = Stat_Node(path);
     171
     172      /*
     173       * If there are errors other than not existing we are finished.
     174       */
     175      if (r < 0 && errno != ENOENT) {
     176        Print_Error(printer, "stat", path);
     177        return -1;
     178      }
     179
     180      /*
     181       * If a file remove and create a directory if not the end.
     182       */
     183      if (r == REGTYPE) {
     184        r = unlink(path);
     185        if (r < 0) {
     186          Print_Error(printer, "unlink", path);
     187          free(copy);
     188          return -1;
     189        }
     190        if (!path_end) {
     191          r = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
     192          if (r < 0) {
     193            Print_Error(printer, "mkdir", path);
     194            free(copy);
     195            return -1;
     196          }
     197        }
     198      }
     199      else if (r < 0) {
     200        /*
     201         * Node does not exist which means the rest of the path will not exist.
     202         */
     203        while (!path_end) {
     204          r = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
     205          if (r < 0) {
     206            Print_Error(printer, "mkdir", path);
     207            free(copy);
     208            return -1;
     209          }
     210          if (!path_end) {
     211            *end = '/';
     212            ++end;
     213          }
     214          while (*end != '\0' && *end != '/')
     215            ++end;
     216          if (*end == '\0')
     217            path_end = true;
     218        }
     219      }
     220      else if (path_end && r == DIRTYPE && !end_is_dir) {
     221        /*
     222         * We only handle a directory if at the end of the path and the end is
     223         * a file. If we cannot remove the directory because it is not empty we
     224         * raise an error. Otherwise this is a directory and we do nothing
     225         * which lets us decend into it.
     226         */
     227        r = rmdir(path);
     228        if (r < 0) {
     229          Print_Error(printer, "rmdir", path);
     230          free(copy);
     231          return -1;
     232        }
     233      }
     234
     235      /*
     236       * If not the end of the path put back the directory separator.
     237       */
     238      if (!path_end) {
     239        *end = '/';
     240        ++end;
     241      }
     242    }
     243  }
     244
     245  free(copy);
     246
     247  return 0;
     248}
     249
     250static int
     251Untar_ProcessHeader(
     252  const char          *bufr,
     253  char                *fname,
     254  unsigned long       *file_size,
     255  unsigned long       *nblocks,
     256  unsigned char       *linkflag,
     257  const rtems_printer *printer
     258)
     259{
     260  char           linkname[100];
     261  int            sum;
     262  int            hdr_chksum;
     263  int            retval = UNTAR_SUCCESSFUL;
     264
     265  fname[0] = '\0';
     266  *file_size = 0;
     267  *nblocks = 0;
     268  *linkflag = -1;
     269
     270  if (strncmp(&bufr[257], "ustar", 5)) {
     271    return UNTAR_SUCCESSFUL;
     272  }
     273
     274  /*
     275   * Compute the TAR checksum and check with the value in the archive.  The
     276   * checksum is computed over the entire header, but the checksum field is
     277   * substituted with blanks.
     278   */
     279  hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
     280  sum        = _rtems_tar_header_checksum(bufr);
     281
     282  if (sum != hdr_chksum) {
     283    rtems_printf(printer, "untar: file header checksum error\n");
     284    return UNTAR_INVALID_CHECKSUM;
     285  }
     286
     287  strncpy(fname, bufr, MAX_NAME_FIELD_SIZE);
     288  fname[MAX_NAME_FIELD_SIZE] = '\0';
     289
     290  *linkflag   = bufr[156];
     291  *file_size = _rtems_octal2ulong(&bufr[124], 12);
     292
     293  /*
     294   * We've decoded the header, now figure out what it contains and do something
     295   * with it.
     296   */
     297  if (*linkflag == SYMTYPE) {
     298    strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE);
     299    linkname[MAX_NAME_FIELD_SIZE] = '\0';
     300    rtems_printf(printer, "untar: symlink: %s -> %s\n", linkname, fname);
     301    symlink(linkname, fname);
     302  } else if (*linkflag == REGTYPE) {
     303    rtems_printf(printer, "untar: file: %s (%i)\n", fname, (int) *file_size);
     304    *nblocks = (((*file_size) + 511) & ~511) / 512;
     305    if (Make_Path(printer, fname, false) < 0) {
     306      retval  = UNTAR_FAIL;
     307    }
     308  } else if (*linkflag == DIRTYPE) {
     309    int r;
     310    rtems_printf(printer, "untar: dir: %s\n", fname);
     311    if (Make_Path(printer, fname, true) < 0) {
     312      retval  = UNTAR_FAIL;
     313    }
     314    r = mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO);
     315    if (r < 0) {
     316      if (errno == EEXIST) {
     317        struct stat stat_buf;
     318        if (stat(fname, &stat_buf) == 0) {
     319          if (!S_ISDIR(stat_buf.st_mode)) {
     320            r = unlink(fname);
     321            if (r == 0) {
     322              r = mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO);
     323            }
     324          }
     325        }
     326      }
     327      if (r < 0) {
     328        Print_Error(printer, "mkdir", fname);
     329        retval = UNTAR_FAIL;
     330      }
     331    }
     332  }
     333
     334  return retval;
     335}
     336
     337/*
    95338 * Function: Untar_FromMemory
    96339 *
     
    115358 */
    116359int
    117 Untar_FromMemory(
    118   void   *tar_buf,
    119   size_t  size
     360Untar_FromMemory_Print(
     361  void                *tar_buf,
     362  size_t               size,
     363  const rtems_printer *printer
    120364)
    121365{
     
    123367  const char     *tar_ptr = (const char *)tar_buf;
    124368  const char     *bufr;
    125   size_t         n;
    126369  char           fname[100];
    127   char           linkname[100];
    128   int            sum;
    129   int            hdr_chksum;
    130   int            retval;
     370  int            retval = UNTAR_SUCCESSFUL;
    131371  unsigned long  ptr;
    132   unsigned long  i;
    133372  unsigned long  nblocks;
    134373  unsigned long  file_size;
    135374  unsigned char  linkflag;
    136375
     376  rtems_printf(printer, "untar: memory at %p (%zu)\n", tar_buf, size);
     377
    137378  ptr = 0;
    138   while (1) {
     379  while (true) {
    139380    if (ptr + 512 > size) {
    140381      retval = UNTAR_SUCCESSFUL;
     
    145386    bufr = &tar_ptr[ptr];
    146387    ptr += 512;
    147     if (strncmp(&bufr[257], "ustar", 5)) {
    148       retval = UNTAR_SUCCESSFUL;
     388
     389    retval = Untar_ProcessHeader(bufr, fname, &file_size, &nblocks, &linkflag, printer);
     390
     391    if (retval != UNTAR_SUCCESSFUL)
    149392      break;
    150     }
    151 
    152     strncpy(fname, bufr, MAX_NAME_FIELD_SIZE);
    153     fname[MAX_NAME_FIELD_SIZE] = '\0';
    154 
    155     linkflag   = bufr[156];
    156     file_size  = _rtems_octal2ulong(&bufr[124], 12);
    157 
    158     /*
    159      * Compute the TAR checksum and check with the value in
    160      * the archive.  The checksum is computed over the entire
    161      * header, but the checksum field is substituted with blanks.
    162      */
    163     hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
    164     sum = _rtems_tar_header_checksum(bufr);
    165 
    166     if (sum != hdr_chksum) {
    167       retval = UNTAR_INVALID_CHECKSUM;
    168       break;
    169     }
    170 
    171     /*
    172      * We've decoded the header, now figure out what it contains and
    173      * do something with it.
    174      */
    175     if (linkflag == SYMTYPE) {
    176       strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE);
    177       linkname[MAX_NAME_FIELD_SIZE] = '\0';
    178       symlink(linkname, fname);
    179     } else if (linkflag == REGTYPE) {
    180       nblocks = (((file_size) + 511) & ~511) / 512;
     393
     394    if (linkflag == REGTYPE) {
    181395      if ((fp = fopen(fname, "w")) == NULL) {
    182         printk("Untar: failed to create file %s\n", fname);
     396        Print_Error(printer, "open", fname);
    183397        ptr += 512 * nblocks;
    184398      } else {
    185399        unsigned long sizeToGo = file_size;
    186         size_t len;
     400        size_t        len;
     401        size_t        i;
     402        size_t        n;
    187403
    188404        /*
    189          * Read out the data.  There are nblocks of data where nblocks
    190          * is the file_size rounded to the nearest 512-byte boundary.
     405         * Read out the data.  There are nblocks of data where nblocks is the
     406         * file_size rounded to the nearest 512-byte boundary.
    191407         */
    192         for (i=0; i<nblocks; i++) {
    193           len = ((sizeToGo < 512L)?(sizeToGo):(512L));
     408        for (i = 0; i < nblocks; i++) {
     409          len = ((sizeToGo < 512L) ? (sizeToGo) : (512L));
    194410          n = fwrite(&tar_ptr[ptr], 1, len, fp);
    195411          if (n != len) {
    196             printk("untar: Error during write\n");
     412            Print_Error(printer, "write", fname);
    197413            retval  = UNTAR_FAIL;
    198414            break;
     
    203419        fclose(fp);
    204420      }
    205     } else if (linkflag == DIRTYPE) {
    206       if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) != 0 ) {
    207         if (errno == EEXIST) {
    208           struct stat stat_buf;
    209           if ( stat(fname, &stat_buf) == 0 ) {
    210             if (  S_ISDIR(stat_buf.st_mode) ) {
    211               continue;
    212             } else {
    213               if ( unlink(fname) != -1 ) {
    214                 if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0 )
    215                   continue;
    216               }
    217             }
    218           }
    219         }
    220         printk("Untar: failed to create directory %s\n", fname);
    221         retval = UNTAR_FAIL;
    222         break;
    223       }
    224     }
    225   }
    226 
    227   return(retval);
    228 }
    229 
    230 /*
    231  * Function: Untar_FromFile
     421
     422    }
     423  }
     424
     425  return retval;
     426}
     427
     428/*
     429 * Function: Untar_FromMemory
    232430 *
    233431 * Description:
    234432 *
    235433 *    This is a simple subroutine used to rip links, directories, and
    236  *    files out of a TAR file.
     434 *    files out of a block of memory.
     435 *
    237436 *
    238437 * Inputs:
    239438 *
    240  *    const char *tar_name   - TAR filename.
     439 *    void *  tar_buf    - Pointer to TAR buffer.
     440 *    size_t  size       - Length of TAR buffer.
     441 *
    241442 *
    242443 * Output:
     
    245446 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
    246447 *          UNTAR_INVALID_HEADER    for an invalid header.
     448 *
    247449 */
    248450int
    249 Untar_FromFile(
    250   const char *tar_name
     451Untar_FromMemory(
     452  void   *tar_buf,
     453  size_t  size
     454)
     455{
     456  return Untar_FromMemory_Print(tar_buf, size, false);
     457}
     458
     459/*
     460 * Function: Untar_FromFile
     461 *
     462 * Description:
     463 *
     464 *    This is a simple subroutine used to rip links, directories, and
     465 *    files out of a TAR file.
     466 *
     467 * Inputs:
     468 *
     469 *    const char *tar_name   - TAR filename.
     470 *
     471 * Output:
     472 *
     473 *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
     474 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
     475 *          UNTAR_INVALID_HEADER    for an invalid header.
     476 */
     477int
     478Untar_FromFile_Print(
     479  const char          *tar_name,
     480  const rtems_printer *printer
    251481)
    252482{
     
    255485  ssize_t        n;
    256486  char           fname[100];
    257   char           linkname[100];
    258   int            sum;
    259   int            hdr_chksum;
    260487  int            retval;
    261488  unsigned long  i;
    262489  unsigned long  nblocks;
    263   unsigned long  size;
     490  unsigned long  file_size;
    264491  unsigned char  linkflag;
    265492
     
    283510    }
    284511
    285     if (strncmp(&bufr[257], "ustar", 5)) {
     512    retval = Untar_ProcessHeader(bufr, fname, &file_size, &nblocks, &linkflag, printer);
     513
     514    if (retval != UNTAR_SUCCESSFUL)
    286515      break;
    287     }
    288 
    289     strncpy(fname, bufr, MAX_NAME_FIELD_SIZE);
    290     fname[MAX_NAME_FIELD_SIZE] = '\0';
    291 
    292     linkflag   = bufr[156];
    293     size       = _rtems_octal2ulong(&bufr[124], 12);
    294 
    295     /*
    296      * Compute the TAR checksum and check with the value in
    297      * the archive.  The checksum is computed over the entire
    298      * header, but the checksum field is substituted with blanks.
    299      */
    300     hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
    301     sum = _rtems_tar_header_checksum(bufr);
    302 
    303     if (sum != hdr_chksum) {
    304       retval = UNTAR_INVALID_CHECKSUM;
    305       break;
    306     }
    307 
    308     /*
    309      * We've decoded the header, now figure out what it contains and
    310      * do something with it.
    311      */
    312     if (linkflag == SYMTYPE) {
    313       strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE);
    314       linkname[MAX_NAME_FIELD_SIZE] = '\0';
    315       symlink(linkname,fname);
    316     } else if (linkflag == REGTYPE) {
     516
     517    if (linkflag == REGTYPE) {
    317518      int out_fd;
    318519
     
    321522       * is the size rounded to the nearest 512-byte boundary.
    322523       */
    323       nblocks = (((size) + 511) & ~511) / 512;
    324524
    325525      if ((out_fd = creat(fname, 0644)) == -1) {
    326         (void) lseek(fd, SEEK_CUR, 512 * nblocks);
     526        (void) lseek(fd, SEEK_CUR, 512UL * nblocks);
    327527      } else {
    328         for (i=0; i<nblocks; i++) {
     528        for (i = 0; i < nblocks; i++) {
    329529          n = read(fd, bufr, 512);
    330           n = MIN(n, size - i*512);
     530          n = MIN(n, file_size - (i * 512UL));
    331531          (void) write(out_fd, bufr, n);
    332532        }
    333533        close(out_fd);
    334534      }
    335     } else if (linkflag == DIRTYPE) {
    336       if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) != 0 ) {
    337         if (errno == EEXIST) {
    338           struct stat stat_buf;
    339           if ( stat(fname, &stat_buf) == 0 ) {
    340             if (  S_ISDIR(stat_buf.st_mode) ) {
    341               continue;
    342             } else {
    343               if ( unlink(fname) != -1 ) {
    344                 if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0 )
    345                   continue;
    346               }
    347             }
    348           }
    349         }
    350       }
    351     }
    352   }
     535    }
     536  }
     537
    353538  free(bufr);
    354539  close(fd);
    355540
    356   return(retval);
     541  return retval;
     542}
     543
     544/*
     545 * Function: Untar_FromFile
     546 *
     547 * Description:
     548 *
     549 *    This is a simple subroutine used to rip links, directories, and
     550 *    files out of a TAR file.
     551 *
     552 * Inputs:
     553 *
     554 *    const char *tar_name   - TAR filename.
     555 *
     556 * Output:
     557 *
     558 *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
     559 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
     560 *          UNTAR_INVALID_HEADER    for an invalid header.
     561 */
     562int
     563Untar_FromFile(
     564  const char *tar_name
     565)
     566{
     567  return Untar_FromFile_Print(tar_name, NULL);
    357568}
    358569
  • cpukit/libmisc/untar/untar.h

    r560db810 rd84e346  
    33 *
    44 * @brief Untar an Image
    5  * 
     5 *
    66 * This file defines the interface to methods which can untar an image.
    77 */
     
    1818#define _RTEMS_UNTAR_H
    1919
     20#include <stdbool.h>
    2021#include <stddef.h>
    2122#include <tar.h>
     23
     24#include <rtems/print.h>
    2225
    2326/**
     
    3841
    3942int Untar_FromMemory(void *tar_buf, size_t size);
     43int Untar_FromMemory_Print(void *tar_buf, size_t size, const rtems_printer* printer);
    4044int Untar_FromFile(const char *tar_name);
     45int Untar_FromFile_Print(const char *tar_name, const rtems_printer* printer);
    4146
    4247/**************************************************************************
  • testsuites/libtests/tar01/init.c

    r560db810 rd84e346  
    4545{
    4646  rtems_status_code sc;
     47  rtems_printer     printer;
     48
     49  rtems_print_printer_printf(&printer);
    4750
    4851  printf("Untaring from memory - ");
    49   sc = Untar_FromMemory((void *)TARFILE_START, TARFILE_SIZE);
     52  sc = Untar_FromMemory_Print((void *)TARFILE_START, TARFILE_SIZE, &printer);
    5053  if (sc != RTEMS_SUCCESSFUL) {
    5154    printf ("error: untar failed: %s\n", rtems_status_text (sc));
     
    5760  printf( "========= /home/test_file =========\n" );
    5861  test_cat( "/home/test_file", 0, 0 );
    59  
     62
    6063  /******************/
    6164  printf( "========= /symlink =========\n" );
     
    98101  printf( "========= /dest/home/test_file =========\n" );
    99102  test_cat( "/dest/home/test_file", 0, 0 );
    100  
     103
    101104  /******************/
    102105  printf( "========= /dest/symlink =========\n" );
Note: See TracChangeset for help on using the changeset viewer.