source: rtems/testsuites/libtests/tar01/init.c @ ffc57e3

Last change on this file since ffc57e3 was ffc57e3, checked in by Christian Mauderer <christian.mauderer@…>, on 12/01/21 at 15:39:46

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

  • Property mode set to 100644
File size: 13.6 KB
Line 
1/*
2 *  COPYRIGHT (c) 1989-2012.
3 *  On-Line Applications Research Corporation (OAR).
4 *
5 *  The license and distribution terms for this file may be
6 *  found in the file LICENSE in this distribution or at
7 *  http://www.rtems.org/license/LICENSE.
8 */
9
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
37#ifdef HAVE_CONFIG_H
38#include "config.h"
39#endif
40
41#include <bsp.h> /* for device driver prototypes */
42#include "tmacros.h"
43#include <rtems/untar.h>
44#include <rtems/error.h>
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <sys/types.h>
49#include <sys/stat.h>
50#include <fcntl.h>
51#include <unistd.h>
52
53#include "tar01-tar.h"
54#include "tar01-tar-gz.h"
55#if HAVE_XZ
56#include "tar01-tar-xz.h"
57#endif
58
59const char rtems_test_name[] = "TAR 1";
60
61/* forward declarations to avoid warnings */
62rtems_task Init(rtems_task_argument argument);
63void test_untar_from_memory(void);
64void test_untar_from_file(void);
65void test_untar_chunks_from_memory(void);
66void test_untar_unzip_tgz(void);
67void test_untar_unzip_txz(void);
68
69#define TARFILE_START    tar01_tar
70#define TARFILE_SIZE     tar01_tar_size
71#define TARFILE_GZ_START tar01_tar_gz
72#define TARFILE_GZ_SIZE  tar01_tar_gz_size
73#if HAVE_XZ
74#define TARFILE_XZ_START tar01_tar_xz
75#define TARFILE_XZ_SIZE  tar01_tar_xz_size
76#endif
77
78void test_cat(
79  char *file,
80  int   offset_arg,
81  int   length
82);
83
84static void test_untar_check_mode(const char* file, int mode)
85{
86  struct stat sb;
87  int         fmode;
88  rtems_test_assert(stat(file, &sb) == 0);
89  fmode = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
90  printf(" %s: mode: %04o want: %04o\n", file, fmode, mode);
91  rtems_test_assert(fmode == mode);
92}
93
94void test_untar_from_memory(void)
95{
96  rtems_status_code sc;
97
98  printf("Untaring from memory - ");
99  sc = Untar_FromMemory_Print(
100    (void *)TARFILE_START,
101    TARFILE_SIZE,
102    &rtems_test_printer
103  );
104  if (sc != RTEMS_SUCCESSFUL) {
105    printf ("error: untar failed: %s\n", rtems_status_text (sc));
106    exit(1);
107  }
108  printf ("successful\n");
109
110  /******************/
111  printf( "========= /home/test_file =========\n" );
112  test_cat( "/home/test_file", 0, 0 );
113
114  /******************/
115  printf( "========= /home/abc/def/test_script =========\n" );
116  test_cat( "/home/abc/def/test_script", 0, 0 );
117  test_untar_check_mode("/home/abc/def/test_script", 0755);
118
119  /******************/
120  printf( "========= /symlink =========\n" );
121  test_cat( "/symlink", 0, 0 );
122
123}
124
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
203void test_untar_from_file(void)
204{
205  int                fd;
206  int                rv;
207  ssize_t            n;
208
209  puts( "" );
210
211  puts( "Copy tar image to test.tar" );
212  /* Copy tar image from object to file in IMFS */
213  fd = open( "/test.tar", O_CREAT|O_TRUNC|O_WRONLY, 0777 );
214  rtems_test_assert( fd != -1 );
215
216  n = write( fd, TARFILE_START, TARFILE_SIZE );
217  rtems_test_assert( n == TARFILE_SIZE );
218  close( fd );
219
220  /* make a directory to untar it into */
221  rv = mkdir( "/dest", 0777 );
222  rtems_test_assert( rv == 0 );
223
224  rv = chdir( "/dest" );
225  rtems_test_assert( rv == 0 );
226
227  /* Case 1: Untar it into empty directory */
228  rv = Untar_FromFile( "/test.tar" );
229  printf("Untaring from file - ");
230  if (rv != UNTAR_SUCCESSFUL) {
231    printf ("error: untar failed: %i\n", rv);
232    exit(1);
233  }
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();
326  printf ("successful\n");
327
328  /******************/
329  printf( "========= /dest/home/test_file =========\n" );
330  test_cat( "/dest/home/test_file", 0, 0 );
331
332  /******************/
333  printf( "========= /dest/home/abc/def/test_script =========\n" );
334  test_cat( "/dest/home/abc/def/test_script", 0, 0 );
335  test_untar_check_mode("/dest/home/abc/def/test_script", 0755);
336
337  /******************/
338  printf( "========= /dest/symlink =========\n" );
339  test_cat( "/dest/symlink", 0, 0 );
340}
341
342void test_untar_chunks_from_memory(void)
343{
344  rtems_status_code sc;
345  int rv;
346  Untar_ChunkContext ctx;
347  unsigned long counter = 0;
348  char *buffer = (char *)TARFILE_START;
349  size_t buflen = TARFILE_SIZE;
350
351  puts( "" );
352
353  /* make a directory to untar it into */
354  rv = mkdir( "/dest2", 0777 );
355  rtems_test_assert( rv == 0 );
356
357  rv = chdir( "/dest2" );
358  rtems_test_assert( rv == 0 );
359
360  printf( "Untaring chunks from memory - " );
361  Untar_ChunkContext_Init(&ctx);
362  do {
363    sc = Untar_FromChunk_Print(
364      &ctx,
365      &buffer[counter],
366      (size_t)1 ,
367      &rtems_test_printer
368    );
369    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
370    counter ++;
371  } while (counter < buflen);
372  printf("successful\n");
373
374  /******************/
375  printf( "========= /dest2/home/test_file =========\n" );
376  test_cat( "/dest2/home/test_file", 0, 0 );
377
378  /******************/
379  printf( "========= /dest2/home/abc/def/test_script =========\n" );
380  test_cat( "/dest2/home/abc/def/test_script", 0, 0 );
381  test_untar_check_mode("/dest2/home/abc/def/test_script", 0755);
382
383  /******************/
384  printf( "========= /dest2/symlink =========\n" );
385  test_cat( "/dest2/symlink", 0, 0 );
386
387}
388
389void test_untar_unzip_tgz(void)
390{
391  int status;
392  int rv;
393  Untar_GzChunkContext ctx;
394  size_t i = 0;
395  char *buffer = (char *)TARFILE_GZ_START;
396  size_t buflen = TARFILE_GZ_SIZE;
397  char inflate_buffer;
398
399  puts( "" );
400
401  rtems_test_assert( buflen != 0 );
402
403  /* make a directory to untar it into */
404  rv = mkdir( "/dest3", 0777 );
405  rtems_test_assert( rv == 0 );
406
407  rv = chdir( "/dest3" );
408  rtems_test_assert( rv == 0 );
409
410  printf( "Untaring chunks from tgz - " );
411
412  status = Untar_GzChunkContext_Init(&ctx, &inflate_buffer, 1);
413  rtems_test_assert(status == UNTAR_SUCCESSFUL);
414  for(i = 0; i < buflen; i++) {
415    status = Untar_FromGzChunk_Print(&ctx, &buffer[i], 1, &rtems_test_printer);
416    rtems_test_assert(status == UNTAR_SUCCESSFUL);
417  }
418  printf( "successful\n" );
419
420  /******************/
421  printf( "========= /dest3/home/test_file =========\n" );
422  test_cat( "/dest3/home/test_file", 0, 0 );
423
424  /******************/
425  printf( "========= /dest3/home/abc/def/test_script =========\n" );
426  test_cat( "/dest3/home/abc/def/test_script", 0, 0 );
427  test_untar_check_mode("/dest3/home/abc/def/test_script", 0755);
428
429  /******************/
430  printf( "========= /dest3/symlink =========\n" );
431  test_cat( "/dest3/symlink", 0, 0 );
432}
433
434void test_untar_unzip_txz(void)
435{
436#if HAVE_XZ
437  int status;
438  int rv;
439  Untar_XzChunkContext ctx;
440  size_t i = 0;
441  char *buffer = (char *)TARFILE_XZ_START;
442  size_t buflen = TARFILE_XZ_SIZE;
443  char inflate_buffer;
444
445  puts( "" );
446
447  rtems_test_assert( buflen != 0 );
448
449  /* make a directory to untar it into */
450  rv = mkdir( "/dest4", 0777 );
451  rtems_test_assert( rv == 0 );
452
453  rv = chdir( "/dest4" );
454  rtems_test_assert( rv == 0 );
455
456  printf( "Untaring chunks from txz - " );
457
458  /*
459   * Use 8K dict, this is set on the command line of xz when compressing.
460   */
461  status = Untar_XzChunkContext_Init(&ctx, XZ_DYNALLOC,
462                                     8 * 1024, &inflate_buffer, 1);
463  rtems_test_assert(status == UNTAR_SUCCESSFUL);
464  for(i = 0; i < buflen; i++) {
465    status = Untar_FromXzChunk_Print(&ctx, &buffer[i], 1, &rtems_test_printer);
466    rtems_test_assert(status == UNTAR_SUCCESSFUL);
467  }
468  printf( "successful\n" );
469
470  /******************/
471  printf( "========= /dest4/home/test_file =========\n" );
472  test_cat( "/dest4/home/test_file", 0, 0 );
473
474  /******************/
475  printf( "========= /dest4/home/abc/def/test_script =========\n" );
476  test_cat( "/dest4/home/abc/def/test_script", 0, 0 );
477  test_untar_check_mode("/dest4/home/abc/def/test_script", 0755);
478
479  /******************/
480  printf( "========= /dest4/symlink =========\n" );
481  test_cat( "/dest4/symlink", 0, 0 );
482#endif
483}
484
485rtems_task Init(
486  rtems_task_argument ignored
487)
488{
489  TEST_BEGIN();
490
491  test_untar_from_memory();
492  test_untar_from_file();
493  test_untar_chunks_from_memory();
494  test_untar_unzip_tgz();
495  test_untar_unzip_txz();
496
497  TEST_END();
498  exit( 0 );
499}
500
501
502/* NOTICE: the clock driver is explicitly disabled */
503#define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER
504#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
505
506#define CONFIGURE_MAXIMUM_TASKS            1
507#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 5
508
509#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
510
511#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
512
513#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
514
515#define CONFIGURE_INIT
516#include <rtems/confdefs.h>
Note: See TracBrowser for help on using the repository browser.