static size_t bb_full_fd_action(int src_fd, int dst_fd, const size_t size2) { int status; size_t xread, wrote, total, size = size2; if (src_fd < 0) { return -1; } if (size == 0) { /* If size is 0 copy until EOF */ size = ULONG_MAX; } { RESERVE_CONFIG_BUFFER(buffer,BUFSIZ); total = 0; wrote = 0; status = -1; while (total < size) { xread = BUFSIZ; if (size < (total + BUFSIZ)) xread = size - total; xread = bb_full_read(src_fd, buffer, xread); if (xread > 0) { if (dst_fd < 0) { /* A -1 dst_fd means we need to fake it... */ wrote = xread; } else { wrote = bb_full_write(dst_fd, buffer, xread); } if (wrote < xread) { bb_perror_msg(bb_msg_write_error); break; } total += wrote; } else if (xread < 0) { bb_perror_msg(bb_msg_read_error); break; } else if (xread == 0) { /* All done. */ status = 0; break; } } RELEASE_CONFIG_BUFFER(buffer); } if (status == 0 || total) return total; /* Some sortof error occured */ return -1; }
extern char get_header_tar(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; union { /* ustar header, Posix 1003.1 */ unsigned char raw[512]; struct { char name[100]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[100]; /* 157-256 */ char magic[6]; /* 257-262 */ char version[2]; /* 263-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 */ } formated; } tar; long sum = 0; long i; /* Align header */ data_align(archive_handle, 512); if (bb_full_read(archive_handle->src_fd, tar.raw, 512) != 512) { /* Assume end of file */ return(EXIT_FAILURE); } archive_handle->offset += 512; /* If there is no filename its an empty header */ if (tar.formated.name[0] == 0) { return(EXIT_SUCCESS); } /* Check header has valid magic, "ustar" is for the proper tar * 0's are for the old tar format */ if (strncmp(tar.formated.magic, "ustar", 5) != 0) { #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) #endif bb_error_msg_and_die("Invalid tar magic"); } /* Do checksum on headers */ for (i = 0; i < 148 ; i++) { sum += tar.raw[i]; } sum += ' ' * 8; for (i = 156; i < 512 ; i++) { sum += tar.raw[i]; } if (sum != strtol(tar.formated.chksum, NULL, 8)) { bb_error_msg("Invalid tar header checksum"); return(EXIT_FAILURE); } #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS if (longname) { file_header->name = longname; longname = NULL; } else if (linkname) { file_header->name = linkname; linkname = NULL; } else #endif if (tar.formated.prefix[0] == 0) { file_header->name = strdup(tar.formated.name); } else { file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name); } file_header->uid = strtol(tar.formated.uid, NULL, 8); file_header->gid = strtol(tar.formated.gid, NULL, 8); file_header->size = strtol(tar.formated.size, NULL, 8); file_header->mtime = strtol(tar.formated.mtime, NULL, 8); file_header->link_name = (tar.formated.linkname[0] != '\0') ? bb_xstrdup(tar.formated.linkname) : NULL; file_header->device = (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) + strtol(tar.formated.devminor, NULL, 8)); /* Set bits 0-11 of the files mode */ file_header->mode = 07777 & strtol(tar.formated.mode, NULL, 8); /* Set bits 12-15 of the files mode */ switch (tar.formated.typeflag) { /* busybox identifies hard links as being regular files with 0 size and a link name */ case '1': file_header->mode |= S_IFREG; break; case 'x': case 'g': bb_error_msg_and_die("pax is not tar"); break; case '7': /* Reserved for high performance files, treat as normal file */ case 0: case '0': #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (last_char_is(file_header->name, '/')) { file_header->mode |= S_IFDIR; } else #endif file_header->mode |= S_IFREG; break; case '2': file_header->mode |= S_IFLNK; break; case '3': file_header->mode |= S_IFCHR; break; case '4': file_header->mode |= S_IFBLK; break; case '5': file_header->mode |= S_IFDIR; break; case '6': file_header->mode |= S_IFIFO; break; #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS case 'L': { longname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, longname, file_header->size); longname[file_header->size] = '\0'; archive_handle->offset += file_header->size; return(get_header_tar(archive_handle)); } case 'K': { linkname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, linkname, file_header->size); linkname[file_header->size] = '\0'; archive_handle->offset += file_header->size; file_header->name = linkname; return(get_header_tar(archive_handle)); } case 'D': /* GNU dump dir */ case 'M': /* Continuation of multi volume archive*/ case 'N': /* Old GNU for names > 100 characters */ case 'S': /* Sparse file */ case 'V': /* Volume header */ bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag); #endif default: bb_error_msg("Unknown typeflag: 0x%x", tar.formated.typeflag); } { /* Strip trailing '/' in directories */ /* Must be done after mode is set as '/' is used to check if its a directory */ char *tmp = last_char_is(file_header->name, '/'); if (tmp) { *tmp = '\0'; } } if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(archive_handle->file_header); archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; archive_handle->action_data(archive_handle); archive_handle->passed = llist_add_to(archive_handle->passed, file_header->name); } else { data_skip(archive_handle); } archive_handle->offset += file_header->size; free(file_header->link_name); return(EXIT_SUCCESS); }
static inline int tftp(const int cmd, const struct hostent *host, const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize) { const int cmd_get = cmd & tftp_cmd_get; const int cmd_put = cmd & tftp_cmd_put; const int bb_tftp_num_retries = 5; struct sockaddr_in sa; struct sockaddr_in from; struct timeval tv; socklen_t fromlen; fd_set rfds; char *cp; unsigned short tmp; int socketfd; int len; int opcode = 0; int finished = 0; int timeout = bb_tftp_num_retries; unsigned short block_nr = 1; #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE int want_option_ack = 0; #endif /* Can't use RESERVE_CONFIG_BUFFER here since the allocation * size varies meaning BUFFERS_GO_ON_STACK would fail */ char *buf=xmalloc(tftp_bufsize + 4); tftp_bufsize += 4; if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { bb_perror_msg("socket"); return EXIT_FAILURE; } len = sizeof(sa); memset(&sa, 0, len); bind(socketfd, (struct sockaddr *)&sa, len); sa.sin_family = host->h_addrtype; sa.sin_port = port; memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, sizeof(sa.sin_addr)); /* build opcode */ if (cmd_get) { opcode = TFTP_RRQ; } if (cmd_put) { opcode = TFTP_WRQ; } while (1) { cp = buf; /* first create the opcode part */ *((unsigned short *) cp) = htons(opcode); cp += 2; /* add filename and mode */ if ((cmd_get && (opcode == TFTP_RRQ)) || (cmd_put && (opcode == TFTP_WRQ))) { int too_long = 0; /* see if the filename fits into buf */ /* and fill in packet */ len = strlen(remotefile) + 1; if ((cp + len) >= &buf[tftp_bufsize - 1]) { too_long = 1; } else { safe_strncpy(cp, remotefile, len); cp += len; } if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) { bb_error_msg("too long remote-filename"); break; } /* add "mode" part of the package */ memcpy(cp, "octet", 6); cp += 6; #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE len = tftp_bufsize - 4; /* data block size */ if (len != TFTP_BLOCKSIZE_DEFAULT) { if ((&buf[tftp_bufsize - 1] - cp) < 15) { bb_error_msg("too long remote-filename"); break; } /* add "blksize" + number of blocks */ memcpy(cp, "blksize", 8); cp += 8; cp += snprintf(cp, 6, "%d", len) + 1; want_option_ack = 1; } #endif } /* add ack and data */ if ((cmd_get && (opcode == TFTP_ACK)) || (cmd_put && (opcode == TFTP_DATA))) { *((unsigned short *) cp) = htons(block_nr); cp += 2; block_nr++; if (cmd_put && (opcode == TFTP_DATA)) { len = bb_full_read(localfd, cp, tftp_bufsize - 4); if (len < 0) { bb_perror_msg("read"); break; } if (len != (tftp_bufsize - 4)) { finished++; } cp += len; } } /* send packet */ timeout = bb_tftp_num_retries; /* re-initialize */ do { len = cp - buf; #ifdef CONFIG_FEATURE_TFTP_DEBUG fprintf(stderr, "sending %u bytes\n", len); for (cp = buf; cp < &buf[len]; cp++) fprintf(stderr, "%02x ", (unsigned char)*cp); fprintf(stderr, "\n"); #endif if (sendto(socketfd, buf, len, 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) { bb_perror_msg("send"); len = -1; break; } if (finished && (opcode == TFTP_ACK)) { break; } /* receive packet */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); tv.tv_sec = TFTP_TIMEOUT; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(socketfd, &rfds); switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) { case 1: len = recvfrom(socketfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen); if (len < 0) { bb_perror_msg("recvfrom"); break; } timeout = 0; if (sa.sin_port == port) { sa.sin_port = from.sin_port; } if (sa.sin_port == from.sin_port) { break; } /* fall-through for bad packets! */ /* discard the packet - treat as timeout */ timeout = bb_tftp_num_retries; case 0: bb_error_msg("timeout"); timeout--; if (timeout == 0) { len = -1; bb_error_msg("last timeout"); } break; default: bb_perror_msg("select"); len = -1; } } while (timeout && (len >= 0)); if ((finished) || (len < 0)) { break; } /* process received packet */ opcode = ntohs(*((unsigned short *) buf)); tmp = ntohs(*((unsigned short *) &buf[2])); #ifdef CONFIG_FEATURE_TFTP_DEBUG fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp); #endif if (opcode == TFTP_ERROR) { const char *msg = NULL; if (buf[4] != '\0') { msg = &buf[4]; buf[tftp_bufsize - 1] = '\0'; } else if (tmp < (sizeof(tftp_bb_error_msg) / sizeof(char *))) { msg = tftp_bb_error_msg[tmp]; } if (msg) { bb_error_msg("server says: %s", msg); } break; } #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE if (want_option_ack) { want_option_ack = 0; if (opcode == TFTP_OACK) { /* server seems to support options */ char *res; res = tftp_option_get(&buf[2], len-2, "blksize"); if (res) { int blksize = atoi(res); if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) { if (cmd_put) { opcode = TFTP_DATA; } else { opcode = TFTP_ACK; } #ifdef CONFIG_FEATURE_TFTP_DEBUG fprintf(stderr, "using blksize %u\n", blksize); #endif tftp_bufsize = blksize + 4; block_nr = 0; continue; } } /* FIXME: * we should send ERROR 8 */ bb_error_msg("bad server option"); break; } bb_error_msg("warning: blksize not supported by server" " - reverting to 512"); tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4; } #endif if (cmd_get && (opcode == TFTP_DATA)) { if (tmp == block_nr) { len = bb_full_write(localfd, &buf[4], len - 4); if (len < 0) { bb_perror_msg("write"); break; } if (len != (tftp_bufsize - 4)) { finished++; } opcode = TFTP_ACK; continue; } /* in case the last ack disappeared into the ether */ if ( tmp == (block_nr - 1) ) { --block_nr; opcode = TFTP_ACK; continue; } else if (tmp + 1 == block_nr) { /* Server lost our TFTP_ACK. Resend it */ block_nr = tmp; opcode = TFTP_ACK; continue; } } if (cmd_put && (opcode == TFTP_ACK)) { if (tmp == (unsigned short)(block_nr - 1)) { if (finished) { break; } opcode = TFTP_DATA; continue; } } } #ifdef CONFIG_FEATURE_CLEAN_UP close(socketfd); free(buf); #endif return finished ? EXIT_SUCCESS : EXIT_FAILURE; }
static inline int writeTarFile(const int tar_fd, const int verboseFlag, const unsigned long dereferenceFlag, const llist_t *include, const llist_t *exclude, const int gzip) { #ifdef CONFIG_FEATURE_TAR_GZIP int gzipDataPipe[2] = { -1, -1 }; int gzipStatusPipe[2] = { -1, -1 }; pid_t gzipPid = 0; volatile int vfork_exec_errno = 0; #endif int errorFlag = FALSE; ssize_t size; struct TarBallInfo tbInfo; tbInfo.hlInfoHead = NULL; fchmod(tar_fd, 0644); tbInfo.tarFd = tar_fd; tbInfo.verboseFlag = verboseFlag; /* Store the stat info for the tarball's file, so * can avoid including the tarball into itself.... */ if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) bb_perror_msg_and_die("Couldnt stat tar file"); #ifdef CONFIG_FEATURE_TAR_GZIP if (gzip) { if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0) { bb_perror_msg_and_die("Failed to create gzip pipe"); } signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ # if __GNUC__ /* Avoid vfork clobbering */ (void) &include; (void) &errorFlag; # endif gzipPid = vfork(); if (gzipPid == 0) { dup2(gzipDataPipe[0], 0); close(gzipDataPipe[1]); if (tbInfo.tarFd != 1) dup2(tbInfo.tarFd, 1); close(gzipStatusPipe[0]); fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC); /* close on exec shows sucess */ execl("/bin/gzip", "gzip", "-f", 0); vfork_exec_errno = errno; close(gzipStatusPipe[1]); exit(-1); } else if (gzipPid > 0) { close(gzipDataPipe[0]); close(gzipStatusPipe[1]); while (1) { char buf; int n = bb_full_read(gzipStatusPipe[0], &buf, 1); if (n == 0 && vfork_exec_errno != 0) { errno = vfork_exec_errno; bb_perror_msg_and_die("Could not exec gzip process"); } else if ((n < 0) && (errno == EAGAIN || errno == EINTR)) continue; /* try it again */ break; } close(gzipStatusPipe[0]); tbInfo.tarFd = gzipDataPipe[1]; } else { bb_perror_msg_and_die("Failed to vfork gzip process"); } } #endif tbInfo.excludeList = exclude; /* Read the directory/files and iterate over them one at a time */ while (include) { if (!recursive_action(include->data, TRUE, dereferenceFlag, FALSE, writeFileToTarball, writeFileToTarball, (void *) &tbInfo)) { errorFlag = TRUE; } include = include->link; } /* Write two empty blocks to the end of the archive */ for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++) { write(tbInfo.tarFd, "\0", 1); } /* To be pedantically correct, we would check if the tarball * is smaller than 20 tar blocks, and pad it if it was smaller, * but that isn't necessary for GNU tar interoperability, and * so is considered a waste of space */ /* Hang up the tools, close up shop, head home */ close(tbInfo.tarFd); if (errorFlag) bb_error_msg("Error exit delayed from previous errors"); freeHardLinkInfo(&tbInfo.hlInfoHead); #ifdef CONFIG_FEATURE_TAR_GZIP if (gzip && gzipPid) { if (waitpid(gzipPid, NULL, 0) == -1) printf("Couldnt wait ?"); } #endif return !errorFlag; }