Changeset ffc57e3 in rtems


Ignore:
Timestamp:
Dec 1, 2021, 3:39:46 PM (7 weeks ago)
Author:
Christian Mauderer <christian.mauderer@…>
Branches:
master
Children:
4c20360d
Parents:
39e6f532
git-author:
Christian Mauderer <christian.mauderer@…> (12/01/21 15:39:46)
git-committer:
Christian Mauderer <christian.mauderer@…> (12/09/21 07:23:47)
Message:

untar: Make behavior similar to GNU or BSD tar

RTEMS untar implementation had problems with overwriting or integrating
archives into existing directory structures. This patch adapts the
behavior to mimic that of a GNU tar or BSD tar and extends the tar01
test to check for the behavior. That is:

  • If a directory structure exists, the files from the archive will be integrated. Existing files are overwritten.
  • If a file exists and the archive contains a directory with the same name, the file is removed and a directory is created. In the above example: if l1/l2 is a file it will be overwritten with a new directory.
  • If a directory exists and the archive contains a file with the same name, the directory will be replaced if it is empty. If it contains files, the result is an error.
  • An archive also can contain only a file without the parent directories. If in that case one of the parent directories exists as a file extracting the archive results in an error. In the example: if l1/l2 is a file and the archive doesn't contain the directories but only the file l1/l2/x.txt that would be an error.
  • In case of an error, it is possible that the archive has been partially extracted.

Closes #4568

Files:
5 edited

Legend:

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

    r39e6f532 rffc57e3  
    127127    *p = '\0';
    128128    if (p[1] == '\0') {
    129       /* Speculatively unlink the last component so that it can be re-created */
    130       unlink(path);
    131129      return 0;
    132130    }
    133131
    134132    if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
    135       if (errno == EEXIST || errno == EISDIR) {
     133      if (errno == EEXIST) {
     134        /* If it exists already: Check whether it is a directory */
    136135        struct stat sb;
    137 
    138         if (stat(path, &sb) != 0) {
     136        if (lstat(path, &sb) != 0) {
     137          Print_Error(printer, "lstat", path);
     138          return -1;
     139        } else if (!S_ISDIR(sb.st_mode)) {
     140          rtems_printf(printer,
     141                       "untar: mkdir: %s: exists but is not a directory\n",
     142                       path);
    139143          return -1;
    140144        }
    141 
    142         if (!S_ISDIR(sb.st_mode)) {
    143           if (unlink(path) != 0) {
    144             Print_Error(printer, "unlink", path);
    145             return -1;
    146           }
    147 
    148           if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
    149             Print_Error(printer, "mkdir (unlink)", path);
    150             return -1;
    151           }
    152         }
     145      } else {
     146        Print_Error(printer, "mkdir", path);
     147        return -1;
    153148      }
    154149    }
     
    207202  if (Make_Path(ctx->printer, ctx->file_path) != 0) {
    208203    retval = UNTAR_FAIL;
     204  } else {
     205    /*
     206     * Speculatively unlink. This should unlink everything but non-empty
     207     * directories or write protected stuff.
     208     */
     209    unlink(ctx->file_path);
    209210  }
    210211
     
    226227    r = mkdir(ctx->file_path, ctx->mode);
    227228    if (r != 0) {
    228       Print_Error(ctx->printer, "mkdir", ctx->file_path);
    229       retval = UNTAR_FAIL;
     229      if (errno == EEXIST) {
     230        /* If it exists already: Check whether it is a directory */
     231        struct stat sb;
     232        if (lstat(ctx->file_path, &sb) != 0) {
     233          Print_Error(ctx->printer, "lstat", ctx->file_path);
     234          retval = UNTAR_FAIL;
     235        } else if (!S_ISDIR(sb.st_mode)) {
     236          rtems_printf(ctx->printer,
     237                       "untar: mkdir: %s: exists but is not a directory\n",
     238                       ctx->file_path);
     239          retval = UNTAR_FAIL;
     240        }
     241      } else {
     242        Print_Error(ctx->printer, "mkdir", ctx->file_path);
     243        retval = UNTAR_FAIL;
     244      }
    230245    }
    231246  }
     
    427442
    428443      if ((out_fd = creat(ctx.file_path, ctx.mode)) == -1) {
    429         (void) lseek(fd, SEEK_CUR, 512UL * ctx.nblocks);
     444        /* Couldn't create that file. Abort. */
     445        retval = UNTAR_FAIL;
     446        break;
    430447      } else {
    431448        for (i = 0; i < ctx.nblocks; i++) {
  • testsuites/libtests/tar01/init.c

    r39e6f532 rffc57e3  
    88 */
    99
     10/*
     11 * Note on the used tar file: Generate the file on a system that supports
     12 * symlinks with the following commands (tested on Linux - you might have to
     13 * adapt on other systems):
     14 *
     15 * export WORK=some_work_directory
     16 * rm -r ${WORK}
     17 * mkdir -p ${WORK}/home/abc/def
     18 * mkdir -p ${WORK}/home/dir
     19 * cd ${WORK}
     20 * echo "#! joel" > home/abc/def/test_script
     21 * echo "ls -las /dev" >> home/abc/def/test_script
     22 * chmod 755 home/abc/def/test_script
     23 * echo "This is a test of loading an RTEMS filesystem from an" > home/test_file
     24 * echo "initial tar image." >> home/test_file
     25 * echo "Hello world" >> home/dir/file
     26 * ln -s home/test_file symlink
     27 * tar cf tar01.tar --format=ustar \
     28 *     symlink \
     29 *     home/test_file \
     30 *     home/abc/def/test_script \
     31 *     home/dir
     32 *
     33 * Note that "home/dir" is in the archive as separate directory. "home/abc" is
     34 * only in the archive as a parent of the file "test_script".
     35 */
     36
    1037#ifdef HAVE_CONFIG_H
    1138#include "config.h"
     
    96123}
    97124
     125static void assert_file_content(
     126    const char *name,
     127    const char *expected_content,
     128    ssize_t expected_size
     129)
     130{
     131  char buf[16];
     132  int fd;
     133  int rd;
     134
     135  fd = open(name, O_RDONLY);
     136  rtems_test_assert( fd >= 0 );
     137  do {
     138    rd = read(fd, buf, sizeof(buf));
     139    rtems_test_assert( rd >= 0 );
     140    if (rd > 0) {
     141      rtems_test_assert( expected_size - rd >= 0 );
     142      rtems_test_assert( memcmp(buf, expected_content, rd) == 0 );
     143      expected_content += rd;
     144      expected_size -= rd;
     145    }
     146  } while(rd > 0);
     147  rtems_test_assert( expected_size == 0 );
     148  close(fd);
     149}
     150
     151static void assert_content_like_expected(void)
     152{
     153  const char *directories[] = {
     154    "home",
     155    "home/abc",
     156    "home/abc/def",
     157    "home/dir",
     158  };
     159  const char *symlinks[] = {
     160    "symlink",
     161  };
     162  const struct {
     163    const char *name;
     164    const char *content;
     165  } files[] = {
     166    {
     167      .name = "home/abc/def/test_script",
     168      .content = "#! joel\nls -las /dev\n",
     169    }, {
     170      .name = "home/test_file",
     171      .content = "This is a test of loading an RTEMS filesystem from an\n"
     172                 "initial tar image.\n",
     173    }, {
     174      .name = "home/dir/file",
     175      .content = "Hello world\n",
     176    }
     177  };
     178  size_t i;
     179  struct stat st;
     180
     181  for(i = 0; i < RTEMS_ARRAY_SIZE(directories); ++i) {
     182    lstat(directories[i], &st);
     183    rtems_test_assert( S_ISDIR(st.st_mode) );
     184  }
     185
     186  for(i = 0; i < RTEMS_ARRAY_SIZE(symlinks); ++i) {
     187    lstat(symlinks[i], &st);
     188    rtems_test_assert( S_ISLNK(st.st_mode) );
     189  }
     190
     191  for(i = 0; i < RTEMS_ARRAY_SIZE(files); ++i) {
     192    lstat(files[i].name, &st);
     193    rtems_test_assert( S_ISREG(st.st_mode) );
     194
     195    assert_file_content(
     196        files[i].name,
     197        files[i].content,
     198        strlen(files[i].content)
     199        );
     200  }
     201}
     202
    98203void test_untar_from_file(void)
    99204{
     
    120225  rtems_test_assert( rv == 0 );
    121226
    122   /* Untar it */
     227  /* Case 1: Untar it into empty directory */
    123228  rv = Untar_FromFile( "/test.tar" );
    124229  printf("Untaring from file - ");
     
    127232    exit(1);
    128233  }
     234  assert_content_like_expected();
     235  printf ("successful\n");
     236
     237  /* Case 2: Most files exist */
     238  rv = unlink("/dest/home/test_file");
     239  rtems_test_assert( rv == 0 );
     240
     241  rv = Untar_FromFile( "/test.tar" );
     242  printf("Untar from file into existing structure with one missing file - ");
     243  if (rv != UNTAR_SUCCESSFUL) {
     244    printf ("error: untar failed: %i\n", rv);
     245    exit(1);
     246  }
     247  assert_content_like_expected();
     248  printf ("successful\n");
     249
     250  /* Case 3: An empty directory exists where a file should be */
     251  rv = unlink("/dest/home/test_file");
     252  rtems_test_assert( rv == 0 );
     253  rv = mkdir("/dest/home/test_file", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
     254  rtems_test_assert( rv == 0 );
     255
     256  rv = Untar_FromFile( "/test.tar" );
     257  printf("Untar from file; overwrite empty directory with file - ");
     258  if (rv != UNTAR_SUCCESSFUL) {
     259    printf ("error: untar failed: %i\n", rv);
     260    exit(1);
     261  }
     262  assert_content_like_expected();
     263  printf ("successful\n");
     264
     265  /* Case 4: A file exists where a parent directory should be created */
     266  rv = unlink("/dest/home/abc/def/test_script");
     267  rtems_test_assert( rv == 0 );
     268  rv = unlink("/dest/home/abc/def");
     269  rtems_test_assert( rv == 0 );
     270  rv = unlink("/dest/home/abc");
     271  rtems_test_assert( rv == 0 );
     272  fd = creat("/dest/home/abc", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
     273  rtems_test_assert( fd >= 0 );
     274  close(fd);
     275
     276  rv = Untar_FromFile( "/test.tar" );
     277  printf("Untar from file; file exists where parent dir should be created - ");
     278  if (rv != UNTAR_FAIL) {
     279    printf ("error: untar didn't fail like expected: %i\n", rv);
     280    exit(1);
     281  }
     282  printf ("expected fail\n");
     283  /* cleanup so that the next one works */
     284  rv = unlink("/dest/home/abc");
     285  rtems_test_assert( rv == 0 );
     286
     287  /* Case 5: A non-empty directory exists where a file should be created */
     288  rv = unlink("/dest/home/test_file");
     289  rtems_test_assert( rv == 0 );
     290  rv = mkdir("/dest/home/test_file", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
     291  rtems_test_assert( rv == 0 );
     292  fd = creat("/dest/home/test_file/file",
     293      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
     294  rtems_test_assert( fd >= 0 );
     295  close(fd);
     296
     297  rv = Untar_FromFile( "/test.tar" );
     298  printf("Untar from file; non-empty dir where file should be created - ");
     299  if (rv != UNTAR_FAIL) {
     300    printf ("error: untar didn't fail like expected: %i\n", rv);
     301    exit(1);
     302  }
     303  printf ("expected fail\n");
     304  /* cleanup so that the next one works */
     305  rv = unlink("/dest/home/test_file/file");
     306  rtems_test_assert( rv == 0 );
     307  rv = unlink("/dest/home/test_file");
     308  rtems_test_assert( rv == 0 );
     309
     310  /* Case 6: A file exists where a directory is explicitly in the archive */
     311  rv = unlink("/dest/home/dir/file");
     312  rtems_test_assert( rv == 0 );
     313  rv = unlink("/dest/home/dir");
     314  rtems_test_assert( rv == 0 );
     315  fd = creat("/dest/home/dir", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
     316  rtems_test_assert( fd >= 0 );
     317  close(fd);
     318
     319  rv = Untar_FromFile( "/test.tar" );
     320  printf("Untar from file; overwrite file with explicit directory - ");
     321  if (rv != UNTAR_SUCCESSFUL) {
     322    printf ("error: untar failed: %i\n", rv);
     323    exit(1);
     324  }
     325  assert_content_like_expected();
    129326  printf ("successful\n");
    130327
  • testsuites/libtests/tar01/tar01.scn

    r39e6f532 rffc57e3  
    1 *** TAR01 TEST ***
    2 Untaring from memory - successful
     1*** BEGIN OF TEST TAR 1 ***
     2*** TEST VERSION: 6.0.0.e1efb4eb8a9d6dd5f6f37dafc9feb0a9e6a888f1
     3*** TEST STATE: EXPECTED_PASS
     4*** TEST BUILD: RTEMS_POSIX_API
     5*** TEST TOOLS: 10.3.1 20210409 (RTEMS 6, RSB ad54d1dd3cf8249d9d39deb1dd28b2f294df062d-modified, Newlib eb03ac1)
     6Untaring from memory - untar: memory at 0x11ece8 (10240)
     7untar: symlink: home/test_file -> symlink
     8untar: file: home/test_file (s:73,m:0644)
     9untar: file: home/abc/def/test_script (s:21,m:0755)
     10untar: dir: home/dir
     11untar: file: home/dir/file (s:12,m:0644)
     12successful
    313========= /home/test_file =========
    414(0)This is a test of loading an RTEMS filesystem from an
    515initial tar image.
    616
     17========= /home/abc/def/test_script =========
     18(0)#! joel
     19ls -las /dev
     20
     21 /home/abc/def/test_script: mode: 0755 want: 0755
    722========= /symlink =========
    823(0)This is a test of loading an RTEMS filesystem from an
     
    1227Copy tar image to test.tar
    1328Untaring from file - successful
     29Untar from file into existing structure with one missing file - successful
     30Untar from file; overwrite empty directory with file - successful
     31Untar from file; file exists where parent dir should be created - expected fail
     32Untar from file; non-empty dir where file should be created - expected fail
     33Untar from file; overwrite file with explicit directory - successful
    1434========= /dest/home/test_file =========
    1535(0)This is a test of loading an RTEMS filesystem from an
    1636initial tar image.
    1737
     38========= /dest/home/abc/def/test_script =========
     39(0)#! joel
     40ls -las /dev
     41
     42 /dest/home/abc/def/test_script: mode: 0755 want: 0755
    1843========= /dest/symlink =========
    1944(0)This is a test of loading an RTEMS filesystem from an
     
    2146
    2247
    23 Untaring chunks from memory - untar: dir: home
    24 untar: file: home/test_file (73)
     48Untaring chunks from memory - untar: symlink: home/test_file -> symlink
     49untar: file: home/test_file (s:73,m:0644)
     50untar: file: home/abc/def/test_script (s:21,m:0755)
     51untar: dir: home/dir
     52untar: file: home/dir/file (s:12,m:0644)
    2553successful
    2654========= /dest2/home/test_file =========
     
    2856initial tar image.
    2957
     58========= /dest2/home/abc/def/test_script =========
     59(0)#! joel
     60ls -las /dev
     61
     62 /dest2/home/abc/def/test_script: mode: 0755 want: 0755
    3063========= /dest2/symlink =========
    3164(0)This is a test of loading an RTEMS filesystem from an
     
    3366
    3467
    35 Untaring chunks from tgz- untar: dir: home
    36 untar: file: home/test_file (73)
    37 successful
     68Untaring chunks from tgz - successful
    3869========= /dest3/home/test_file =========
    3970(0)This is a test of loading an RTEMS filesystem from an
    4071initial tar image.
    4172
     73========= /dest3/home/abc/def/test_script =========
     74(0)#! joel
     75ls -las /dev
     76
     77 /dest3/home/abc/def/test_script: mode: 0755 want: 0755
    4278========= /dest3/symlink =========
    4379(0)This is a test of loading an RTEMS filesystem from an
    4480initial tar image.
    45 *** END OF TAR01 TEST ***
     81
     82
     83*** END OF TEST TAR 1 ***
Note: See TracChangeset for help on using the changeset viewer.