/* Change to directory I, in a virtual way. This does not actually invoke chdir; it merely sets chdir_fd to an int suitable as the first argument for openat, etc. If I is 0, change to the initial working directory; otherwise, I must be a value returned by chdir_arg. */ void chdir_do (int i) { if (chdir_current != i) { struct wd *curr = &wd[i]; int fd = curr->fd; if (! fd) { if (! IS_ABSOLUTE_FILE_NAME (curr->name)) chdir_do (i - 1); fd = openat (chdir_fd, curr->name, open_searchdir_flags & ~ O_NOFOLLOW); if (fd < 0) open_fatal (curr->name); curr->fd = fd; /* Add I to the cache, tossing out the lowest-ranking entry if the cache is full. */ if (wdcache_count < CHDIR_CACHE_SIZE) wdcache[wdcache_count++] = i; else { struct wd *stale = &wd[wdcache[CHDIR_CACHE_SIZE - 1]]; if (close (stale->fd) != 0) close_diag (stale->name); stale->fd = 0; wdcache[CHDIR_CACHE_SIZE - 1] = i; } } if (0 < fd) { /* Move the i value to the front of the cache. This is O(CHDIR_CACHE_SIZE), but the cache is small. */ size_t ci; int prev = wdcache[0]; for (ci = 1; prev != i; ci++) { int cur = wdcache[ci]; wdcache[ci] = prev; if (cur == i) break; prev = cur; } wdcache[0] = i; } chdir_current = i; chdir_fd = fd; } }
/* Set ARCHIVE for uncompressing, then reading an archive. */ pid_t sys_child_open_for_uncompress (void) { int parent_pipe[2]; int child_pipe[2]; pid_t grandchild_pid; pid_t child_pid; xpipe (parent_pipe); child_pid = xfork (); if (child_pid > 0) { /* The parent tar is still here! Just clean up. */ archive = parent_pipe[PREAD]; xclose (parent_pipe[PWRITE]); return child_pid; } /* The newborn child tar is here! */ set_program_name (_("tar (child)")); signal (SIGPIPE, SIG_DFL); xdup2 (parent_pipe[PWRITE], STDOUT_FILENO); xclose (parent_pipe[PREAD]); /* Check if we need a grandchild tar. This happens only if either: a) we're reading stdin: to force unblocking; b) the file is to be accessed by rmt: compressor doesn't know how; c) the file is not a plain file. */ if (strcmp (archive_name_array[0], "-") != 0 && !_remdev (archive_name_array[0]) && is_regular_file (archive_name_array[0])) { /* We don't need a grandchild tar. Open the archive and lauch the uncompressor. */ archive = open (archive_name_array[0], O_RDONLY | O_BINARY, MODE_RW); if (archive < 0) open_fatal (archive_name_array[0]); xdup2 (archive, STDIN_FILENO); execlp (use_compress_program_option, use_compress_program_option, "-d", (char *) 0); exec_fatal (use_compress_program_option); } /* We do need a grandchild tar. */ xpipe (child_pipe); grandchild_pid = xfork (); if (grandchild_pid == 0) { /* The newborn grandchild tar is here! Launch the uncompressor. */ set_program_name (_("tar (grandchild)")); xdup2 (child_pipe[PREAD], STDIN_FILENO); xclose (child_pipe[PWRITE]); execlp (use_compress_program_option, use_compress_program_option, "-d", (char *) 0); exec_fatal (use_compress_program_option); } /* The child tar is still here! */ /* Prepare for unblocking the data from the archive into the uncompressor. */ xdup2 (child_pipe[PWRITE], STDOUT_FILENO); xclose (child_pipe[PREAD]); if (strcmp (archive_name_array[0], "-") == 0) archive = STDIN_FILENO; else archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY, MODE_RW, rsh_command_option); if (archive < 0) open_fatal (archive_name_array[0]); /* Let's read the archive and pipe it into stdout. */ while (1) { char *cursor; size_t maximum; size_t count; size_t status; clear_read_error_count (); error_loop: status = rmtread (archive, record_start->buffer, record_size); if (status == SAFE_READ_ERROR) { archive_read_error (); goto error_loop; } if (status == 0) break; cursor = record_start->buffer; maximum = status; while (maximum) { count = maximum < BLOCKSIZE ? maximum : BLOCKSIZE; if (full_write (STDOUT_FILENO, cursor, count) != count) write_error (use_compress_program_option); cursor += count; maximum -= count; } } xclose (STDOUT_FILENO); wait_for_grandchild (grandchild_pid); }
/* Set ARCHIVE for writing, then compressing an archive. */ pid_t sys_child_open_for_compress (void) { int parent_pipe[2]; int child_pipe[2]; pid_t grandchild_pid; pid_t child_pid; xpipe (parent_pipe); child_pid = xfork (); if (child_pid > 0) { /* The parent tar is still here! Just clean up. */ archive = parent_pipe[PWRITE]; xclose (parent_pipe[PREAD]); return child_pid; } /* The new born child tar is here! */ set_program_name (_("tar (child)")); signal (SIGPIPE, SIG_DFL); xdup2 (parent_pipe[PREAD], STDIN_FILENO); xclose (parent_pipe[PWRITE]); /* Check if we need a grandchild tar. This happens only if either: a) the file is to be accessed by rmt: compressor doesn't know how; b) the file is not a plain file. */ if (!_remdev (archive_name_array[0]) && is_regular_file (archive_name_array[0])) { if (backup_option) maybe_backup_file (archive_name_array[0], 1); /* We don't need a grandchild tar. Open the archive and launch the compressor. */ if (strcmp (archive_name_array[0], "-")) { archive = creat (archive_name_array[0], MODE_RW); if (archive < 0) { int saved_errno = errno; if (backup_option) undo_last_backup (); errno = saved_errno; open_fatal (archive_name_array[0]); } xdup2 (archive, STDOUT_FILENO); } execlp (use_compress_program_option, use_compress_program_option, NULL); exec_fatal (use_compress_program_option); } /* We do need a grandchild tar. */ xpipe (child_pipe); grandchild_pid = xfork (); if (grandchild_pid == 0) { /* The newborn grandchild tar is here! Launch the compressor. */ set_program_name (_("tar (grandchild)")); xdup2 (child_pipe[PWRITE], STDOUT_FILENO); xclose (child_pipe[PREAD]); execlp (use_compress_program_option, use_compress_program_option, (char *) 0); exec_fatal (use_compress_program_option); } /* The child tar is still here! */ /* Prepare for reblocking the data from the compressor into the archive. */ xdup2 (child_pipe[PREAD], STDIN_FILENO); xclose (child_pipe[PWRITE]); if (strcmp (archive_name_array[0], "-") == 0) archive = STDOUT_FILENO; else { archive = rmtcreat (archive_name_array[0], MODE_RW, rsh_command_option); if (archive < 0) open_fatal (archive_name_array[0]); } /* Let's read out of the stdin pipe and write an archive. */ while (1) { size_t status = 0; char *cursor; size_t length; /* Assemble a record. */ for (length = 0, cursor = record_start->buffer; length < record_size; length += status, cursor += status) { size_t size = record_size - length; status = safe_read (STDIN_FILENO, cursor, size); if (status == SAFE_READ_ERROR) read_fatal (use_compress_program_option); if (status == 0) break; } /* Copy the record. */ if (status == 0) { /* We hit the end of the file. Write last record at full length, as the only role of the grandchild is doing proper reblocking. */ if (length > 0) { memset (record_start->buffer + length, 0, record_size - length); status = sys_write_archive_buffer (); if (status != record_size) archive_write_error (status); } /* There is nothing else to read, break out. */ break; } status = sys_write_archive_buffer (); if (status != record_size) archive_write_error (status); } wait_for_grandchild (grandchild_pid); }