static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, void *userData, int depth UNUSED_PARAM) { struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; const char *header_name; int inputFileFd = -1; /* Strip leading '/' (must be before memorizing hardlink's name) */ header_name = fileName; while (header_name[0] == '/') { static smallint warned; if (!warned) { bb_error_msg("removing leading '/' from member names"); warned = 1; } header_name++; } if (header_name[0] == '\0') return TRUE; /* It is against the rules to archive a socket */ if (S_ISSOCK(statbuf->st_mode)) { bb_error_msg("%s: socket ignored", fileName); return TRUE; } /* * Check to see if we are dealing with a hard link. * If so - * Treat the first occurance of a given dev/inode as a file while * treating any additional occurances as hard links. This is done * by adding the file information to the HardLinkInfo linked list. */ tbInfo->hlInfo = NULL; if (statbuf->st_nlink > 1) { tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); if (tbInfo->hlInfo == NULL) addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, header_name); } /* It is a bad idea to store the archive we are in the process of creating, * so check the device and inode to be sure that this particular file isn't * the new tarball */ if (tbInfo->statBuf.st_dev == statbuf->st_dev && tbInfo->statBuf.st_ino == statbuf->st_ino ) { bb_error_msg("%s: file is the archive; skipping", fileName); return TRUE; } if (exclude_file(tbInfo->excludeList, header_name)) return SKIP; #if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS if (strlen(header_name) >= NAME_SIZE) { bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); return TRUE; } #endif /* Is this a regular file? */ if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) { /* open the file we want to archive, and make sure all is well */ inputFileFd = open_or_warn(fileName, O_RDONLY); if (inputFileFd < 0) { return FALSE; } } /* Add an entry to the tarball */ if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) { return FALSE; } /* If it was a regular file, write out the body */ if (inputFileFd >= 0) { size_t readSize; /* Write the file to the archive. */ /* We record size into header first, */ /* and then write out file. If file shrinks in between, */ /* tar will be corrupted. So we don't allow for that. */ /* NB: GNU tar 1.16 warns and pads with zeroes */ /* or even seeks back and updates header */ bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); ////off_t readSize; ////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); ////if (readSize != statbuf->st_size && readSize >= 0) { //// bb_error_msg_and_die("short read from %s, aborting", fileName); ////} /* Check that file did not grow in between? */ /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */ close(inputFileFd); /* Pad the file up to the tar block size */ /* (a few tricks here in the name of code size) */ readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1); memset(block_buf, 0, readSize); xwrite(tbInfo->tarFd, block_buf, readSize); } return TRUE; }
static int writeFileToTarball(const char *fileName, struct stat *statbuf, void *userData) { struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; const char *header_name; /* ** Check to see if we are dealing with a hard link. ** If so - ** Treat the first occurance of a given dev/inode as a file while ** treating any additional occurances as hard links. This is done ** by adding the file information to the HardLinkInfo linked list. */ tbInfo->hlInfo = NULL; if (statbuf->st_nlink > 1) { tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); if (tbInfo->hlInfo == NULL) addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName); } /* It is against the rules to archive a socket */ if (S_ISSOCK(statbuf->st_mode)) { bb_error_msg("%s: socket ignored", fileName); return (TRUE); } /* It is a bad idea to store the archive we are in the process of creating, * so check the device and inode to be sure that this particular file isn't * the new tarball */ if (tbInfo->statBuf.st_dev == statbuf->st_dev && tbInfo->statBuf.st_ino == statbuf->st_ino) { bb_error_msg("%s: file is the archive; skipping", fileName); return (TRUE); } header_name = fileName; while (header_name[0] == '/') { static int alreadyWarned = FALSE; if (alreadyWarned == FALSE) { bb_error_msg("Removing leading '/' from member names"); alreadyWarned = TRUE; } header_name++; } if (strlen(fileName) >= NAME_SIZE) { bb_error_msg(bb_msg_name_longer_than_foo, NAME_SIZE); return (TRUE); } if (header_name[0] == '\0') return TRUE; # ifdef CONFIG_FEATURE_TAR_FROM if (exclude_file(tbInfo->excludeList, header_name)) { return SKIP; } # endif /* CONFIG_FEATURE_TAR_FROM */ if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) { return (FALSE); } /* Now, if the file is a regular file, copy it out to the tarball */ if ((tbInfo->hlInfo == NULL) && (S_ISREG(statbuf->st_mode))) { int inputFileFd; ssize_t readSize = 0; /* open the file we want to archive, and make sure all is well */ if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { bb_perror_msg("%s: Cannot open", fileName); return (FALSE); } /* write the file to the archive */ readSize = bb_copyfd_eof(inputFileFd, tbInfo->tarFd); /* Pad the file up to the tar block size */ for (; (readSize % TAR_BLOCK_SIZE) != 0; readSize++) { write(tbInfo->tarFd, "\0", 1); } close(inputFileFd); } return (TRUE); }