static double file_sparseness (const struct stat *p) { if (0 == p->st_size) { if (0 == ST_NBLOCKS(*p)) return 1.0; else return ST_NBLOCKS(*p) < 0 ? -HUGE_VAL : HUGE_VAL; } else { double blklen = ST_NBLOCKSIZE * (double)ST_NBLOCKS(*p); return blklen / p->st_size; } }
/* TODO isolate important code and move to files_lib.c. */ static bool FileIsSparse(const char *filename) { MAYBE_SYNC_NOW; struct stat statbuf; int ret = stat(filename, &statbuf); assert_int_not_equal(ret, -1); Log(LOG_LEVEL_DEBUG, " st_size=%ju ST_NBYTES=%ju ST_NBLOCKS=%ju ST_BLKSIZE=%ju DEV_BSIZE=%ju", (uint64_t) statbuf.st_size, (uint64_t) ST_NBYTES(statbuf), (uint64_t) ST_NBLOCKS(statbuf), (uint64_t) ST_BLKSIZE(statbuf), (uint64_t) DEV_BSIZE); if (statbuf.st_size <= ST_NBYTES(statbuf)) { Log(LOG_LEVEL_DEBUG, "File is probably non-sparse"); return false; } else { /* We definitely know the file is sparse, since the allocated bytes * are less than the real size. */ Log(LOG_LEVEL_DEBUG, "File is definitely sparse"); return true; } }
/* Returns true if the file represented by stat is a sparse one */ bool sparse_file_p (struct tar_stat_info *st) { return (ST_NBLOCKS (st->stat) < (st->stat.st_size / ST_NBLOCKSIZE + (st->stat.st_size % ST_NBLOCKSIZE != 0))); }
/* Scan the sparse file and create its map */ static bool sparse_scan_file (struct tar_sparse_file *file) { struct tar_stat_info *st = file->stat_info; int fd = file->fd; char buffer[BLOCKSIZE]; size_t count = 0; off_t offset = 0; struct sp_array sp = {0, 0}; st->archive_file_size = 0; if (ST_NBLOCKS (st->stat) == 0) offset = st->stat.st_size; else { if (!tar_sparse_scan (file, scan_begin, NULL)) return false; while ((count = blocking_read (fd, buffer, sizeof buffer)) != 0 && count != SAFE_READ_ERROR) { /* Analyze the block. */ if (zero_block_p (buffer, count)) { if (sp.numbytes) { sparse_add_map (st, &sp); sp.numbytes = 0; if (!tar_sparse_scan (file, scan_block, NULL)) return false; } } else { if (sp.numbytes == 0) sp.offset = offset; sp.numbytes += count; st->archive_file_size += count; if (!tar_sparse_scan (file, scan_block, buffer)) return false; } offset += count; } } if (sp.numbytes == 0) sp.offset = offset; sparse_add_map (st, &sp); st->archive_file_size += count; return tar_sparse_scan (file, scan_end, NULL); }
void CheckForFileHoles(struct stat *sstat, Promise *pp) /* Need a transparent way of getting this into CopyReg() */ /* Use a public member in struct Image */ { if (pp == NULL) { return; } #if !defined(MINGW) if (sstat->st_size > sstat->st_blocks * DEV_BSIZE) #else # ifdef HAVE_ST_BLOCKS if (sstat->st_size > sstat->st_blocks * DEV_BSIZE) # else if (sstat->st_size > ST_NBLOCKS((*sstat)) * DEV_BSIZE) # endif #endif { pp->makeholes = 1; /* must have a hole to get checksum right */ } pp->makeholes = 0; }
static long count_entry (char *ent, int top, dev_t last_dev) { long size; if (((top && opt_dereference_arguments) ? stat (ent, &stat_buf) : (*xstat) (ent, &stat_buf)) < 0) { error (0, errno, "%s", path->text); exit_status = 1; return 0; } if (!opt_count_all && stat_buf.st_nlink > 1 && hash_insert (stat_buf.st_ino, stat_buf.st_dev)) return 0; /* Have counted this already. */ if (output_size == size_bytes) size = stat_buf.st_size; else size = ST_NBLOCKS (stat_buf); tot_size += size; if (S_ISDIR (stat_buf.st_mode)) { unsigned pathlen; dev_t dir_dev; char *name_space; char *namep; struct saved_cwd cwd; int through_symlink; struct stat e_buf; dir_dev = stat_buf.st_dev; if (opt_one_file_system && !top && last_dev != dir_dev) return 0; /* Don't enter a new file system. */ #ifndef S_ISDIR # define S_ISDIR(s) 0 #endif /* If we're dereferencing symlinks and we're about to chdir through a symlink, remember the current directory so we can return to it later. In other cases, chdir ("..") works fine. */ through_symlink = (xstat == stat && lstat (ent, &e_buf) == 0 && S_ISLNK (e_buf.st_mode)); if (through_symlink) if (save_cwd (&cwd)) exit (1); if (chdir (ent) < 0) { error (0, errno, _("cannot change to directory %s"), path->text); exit_status = 1; return 0; } errno = 0; name_space = savedir (".", stat_buf.st_size); if (name_space == NULL) { if (errno) { error (0, errno, "%s", path->text); if (through_symlink) { if (restore_cwd (&cwd, "..", path->text)) exit (1); free_cwd (&cwd); } else if (chdir ("..") < 0) error (1, errno, _("cannot change to `..' from directory %s"), path->text); exit_status = 1; return 0; } else error (1, 0, _("virtual memory exhausted")); } /* Remember the current path. */ str_concatc (path, "/"); pathlen = path->length; namep = name_space; while (*namep != 0) { str_concatc (path, namep); size += count_entry (namep, 0, dir_dev); str_trunc (path, pathlen); namep += strlen (namep) + 1; } free (name_space); if (through_symlink) { restore_cwd (&cwd, "..", path->text); free_cwd (&cwd); } else if (chdir ("..") < 0) error (1, errno, _("cannot change to `..' from directory %s"), path->text); str_trunc (path, pathlen - 1); /* Remove the "/" we added. */ if (!opt_summarize_only || top) { if (opt_human_readable) { char buf[LONGEST_HUMAN_READABLE + 1]; printf("%s\t%s\n", human_readable (size, buf, LONGEST_HUMAN_READABLE + 1), path->length > 0 ? path->text : "/"); } else { printf ("%ld\t%s\n", (output_size == size_bytes ? size : convert_blocks (size, output_size)), path->length > 0 ? path->text : "/"); } fflush (stdout); } return opt_separate_dirs ? 0 : size; } else if (opt_all || top) { /* FIXME: make this an option. */ int print_only_dir_size = 0; if (!print_only_dir_size) { if (opt_human_readable) { char buf[LONGEST_HUMAN_READABLE + 1]; printf("%s\t%s\n", human_readable (size, buf, LONGEST_HUMAN_READABLE + 1), path->length > 0 ? path->text : "/"); } else { printf ("%ld\t%s\n", output_size == size_bytes ? size : convert_blocks (size, output_size), path->text); } fflush (stdout); } } return size; }
static void do_fprintf (struct format_val *dest, struct segment *segment, const char *pathname, const struct stat *stat_buf) { char hbuf[LONGEST_HUMAN_READABLE + 1]; const char *cp; switch (segment->segkind) { case KIND_PLAIN: /* Plain text string (no % conversion). */ /* trusted */ checked_fwrite(segment->text, 1, segment->text_len, dest); break; case KIND_STOP: /* Terminate argument and flush output. */ /* trusted */ checked_fwrite (segment->text, 1, segment->text_len, dest); checked_fflush (dest); break; case KIND_FORMAT: switch (segment->format_char[0]) { case 'a': /* atime in `ctime' format. */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, ctime_format (get_stat_atime (stat_buf))); break; case 'b': /* size in 512-byte blocks */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), hbuf, human_ceiling, ST_NBLOCKSIZE, 512)); break; case 'c': /* ctime in `ctime' format */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime (stat_buf))); break; case 'd': /* depth in search tree */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, state.curdepth); break; case 'D': /* Device on which file exists (stat.st_dev) */ /* trusted */ checked_fprintf (dest, segment->text, human_readable ((uintmax_t) stat_buf->st_dev, hbuf, human_ceiling, 1, 1)); break; case 'f': /* base name of path */ /* sanitised */ { char *base = base_name (pathname); checked_print_quoted (dest, segment->text, base); free (base); } break; case 'F': /* file system type */ /* trusted */ checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname)); break; case 'g': /* group name */ /* trusted */ /* (well, the actual group is selected by the user but * its name was selected by the system administrator) */ { struct group *g; g = getgrgid (stat_buf->st_gid); if (g) { segment->text[segment->text_len] = 's'; checked_fprintf (dest, segment->text, g->gr_name); break; } else { /* Do nothing. */ /*FALLTHROUGH*/ } } /*FALLTHROUGH*/ /*...sometimes, so 'G' case.*/ case 'G': /* GID number */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, human_readable ((uintmax_t) stat_buf->st_gid, hbuf, human_ceiling, 1, 1)); break; case 'h': /* leading directories part of path */ /* sanitised */ { cp = strrchr (pathname, '/'); if (cp == NULL) /* No leading directories. */ { /* If there is no slash in the pathname, we still * print the string because it contains characters * other than just '%s'. The %h expands to ".". */ checked_print_quoted (dest, segment->text, "."); } else { char *s = strdup (pathname); s[cp - pathname] = 0; checked_print_quoted (dest, segment->text, s); free (s); } } break; case 'H': /* ARGV element file was found under */ /* trusted */ { char *s = xmalloc (state.starting_path_length+1); memcpy (s, pathname, state.starting_path_length); s[state.starting_path_length] = 0; checked_fprintf (dest, segment->text, s); free (s); } break; case 'i': /* inode number */ /* UNTRUSTED, but not exploitable I think */ /* POSIX does not guarantee that ino_t is unsigned or even * integral (except as an XSI extension), but we'll work on * fixing that if we ever get a report of a system where * ino_t is indeed a signed integral type or a non-integral * arithmetic type. */ checked_fprintf (dest, segment->text, human_readable ((uintmax_t) stat_buf->st_ino, hbuf, human_ceiling, 1, 1)); break; case 'k': /* size in 1K blocks */ /* UNTRUSTED, but not exploitable I think */ checked_fprintf (dest, segment->text, human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), hbuf, human_ceiling, ST_NBLOCKSIZE, 1024)); break; case 'l': /* object of symlink */ /* sanitised */ #ifdef S_ISLNK { char *linkname = 0; if (S_ISLNK (stat_buf->st_mode)) { linkname = areadlinkat (state.cwd_dir_fd, state.rel_pathname); if (linkname == NULL) { nonfatal_target_file_error (errno, pathname); state.exit_status = 1; } } if (linkname) { checked_print_quoted (dest, segment->text, linkname); } else { /* We still need to honour the field width etc., so this is * not a no-op. */ checked_print_quoted (dest, segment->text, ""); } free (linkname); } #endif /* S_ISLNK */ break; case 'M': /* mode as 10 chars (eg., "-rwxr-x--x" */ /* UNTRUSTED, probably unexploitable */ { char modestring[16] ; filemodestring (stat_buf, modestring); modestring[10] = '\0'; checked_fprintf (dest, segment->text, modestring); } break; case 'm': /* mode as octal number (perms only) */ /* UNTRUSTED, probably unexploitable */ { /* Output the mode portably using the traditional numbers, even if the host unwisely uses some other numbering scheme. But help the compiler in the common case where the host uses the traditional numbering scheme. */ mode_t m = stat_buf->st_mode; bool traditional_numbering_scheme = (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000 && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100 && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010 && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001); checked_fprintf (dest, segment->text, (traditional_numbering_scheme ? m & MODE_ALL : ((m & S_ISUID ? 04000 : 0) | (m & S_ISGID ? 02000 : 0) | (m & S_ISVTX ? 01000 : 0) | (m & S_IRUSR ? 00400 : 0) | (m & S_IWUSR ? 00200 : 0) | (m & S_IXUSR ? 00100 : 0) | (m & S_IRGRP ? 00040 : 0) | (m & S_IWGRP ? 00020 : 0) | (m & S_IXGRP ? 00010 : 0) | (m & S_IROTH ? 00004 : 0) | (m & S_IWOTH ? 00002 : 0) | (m & S_IXOTH ? 00001 : 0)))); } break; case 'n': /* number of links */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, human_readable ((uintmax_t) stat_buf->st_nlink, hbuf, human_ceiling, 1, 1)); break; case 'p': /* pathname */ /* sanitised */ checked_print_quoted (dest, segment->text, pathname); break; case 'P': /* pathname with ARGV element stripped */ /* sanitised */ if (state.curdepth > 0) { cp = pathname + state.starting_path_length; if (*cp == '/') /* Move past the slash between the ARGV element and the rest of the pathname. But if the ARGV element ends in a slash, we didn't add another, so we've already skipped past it. */ cp++; } else { cp = ""; } checked_print_quoted (dest, segment->text, cp); break; case 's': /* size in bytes */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, human_readable ((uintmax_t) stat_buf->st_size, hbuf, human_ceiling, 1, 1)); break; case 'S': /* sparseness */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, file_sparseness (stat_buf)); break; case 't': /* mtime in `ctime' format */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, ctime_format (get_stat_mtime (stat_buf))); break; case 'u': /* user name */ /* trusted */ /* (well, the actual user is selected by the user on systems * where chown is not restricted, but the user name was * selected by the system administrator) */ { struct passwd *p; p = getpwuid (stat_buf->st_uid); if (p) { segment->text[segment->text_len] = 's'; checked_fprintf (dest, segment->text, p->pw_name); break; } /* else fallthru */ } /* FALLTHROUGH*/ /* .. to case U */ case 'U': /* UID number */ /* UNTRUSTED, probably unexploitable */ checked_fprintf (dest, segment->text, human_readable ((uintmax_t) stat_buf->st_uid, hbuf, human_ceiling, 1, 1)); break; /* %Y: type of file system entry like `ls -l`: * (d,-,l,s,p,b,c,n) n=nonexistent (symlink) */ case 'Y': /* in case of symlink */ /* trusted */ { #ifdef S_ISLNK if (S_ISLNK (stat_buf->st_mode)) { struct stat sbuf; /* If we would normally follow links, do not do so. * If we would normally not follow links, do so. */ if ((following_links () ? optionp_stat : optionl_stat) (state.rel_pathname, &sbuf) != 0) { if ( errno == ENOENT ) { checked_fprintf (dest, segment->text, "N"); break; } else if ( errno == ELOOP ) { checked_fprintf (dest, segment->text, "L"); break; } else { checked_fprintf (dest, segment->text, "?"); error (0, errno, "%s", safely_quote_err_filename (0, pathname)); /* exit_status = 1; return ; */ break; } } checked_fprintf (dest, segment->text, mode_to_filetype (sbuf.st_mode & S_IFMT)); } #endif /* S_ISLNK */ else { checked_fprintf (dest, segment->text, mode_to_filetype (stat_buf->st_mode & S_IFMT)); } } break; case 'y': /* trusted */ { checked_fprintf (dest, segment->text, mode_to_filetype (stat_buf->st_mode & S_IFMT)); } break; case 'Z': /* SELinux security context */ { security_context_t scontext; int rv = (*options.x_getfilecon) (state.cwd_dir_fd, state.rel_pathname, &scontext); if (rv < 0) { /* If getfilecon fails, there will in the general case still be some text to print. We just make %Z expand to an empty string. */ checked_fprintf (dest, segment->text, ""); error (0, errno, _("getfilecon failed: %s"), safely_quote_err_filename (0, pathname)); state.exit_status = 1; } else { checked_fprintf (dest, segment->text, scontext); freecon (scontext); } } break; case 0: case '%': checked_fprintf (dest, segment->text); break; } /* end of KIND_FORMAT case */ break; } }
int StatFile(ServerConnectionState *conn, char *sendbuffer, char *ofilename) /* Because we do not know the size or structure of remote datatypes,*/ /* the simplest way to transfer the data is to convert them into */ /* plain text and interpret them on the other side. */ { Stat cfst; struct stat statbuf, statlinkbuf; char linkbuf[CF_BUFSIZE], filename[CF_BUFSIZE]; int islink = false; TranslatePath(filename, ofilename); memset(&cfst, 0, sizeof(Stat)); if (strlen(ReadLastNode(filename)) > CF_MAXLINKSIZE) { snprintf(sendbuffer, CF_BUFSIZE, "BAD: Filename suspiciously long [%s]\n", filename); Log(LOG_LEVEL_ERR, "%s", sendbuffer); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } if (lstat(filename, &statbuf) == -1) { snprintf(sendbuffer, CF_BUFSIZE, "BAD: unable to stat file %s", filename); Log(LOG_LEVEL_VERBOSE, "%s. (lstat: %s)", sendbuffer, GetErrorStr()); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } cfst.cf_readlink = NULL; cfst.cf_lmode = 0; cfst.cf_nlink = CF_NOSIZE; memset(linkbuf, 0, CF_BUFSIZE); #ifndef __MINGW32__ // windows doesn't support symbolic links if (S_ISLNK(statbuf.st_mode)) { islink = true; cfst.cf_type = FILE_TYPE_LINK; /* pointless - overwritten */ cfst.cf_lmode = statbuf.st_mode & 07777; cfst.cf_nlink = statbuf.st_nlink; if (readlink(filename, linkbuf, CF_BUFSIZE - 1) == -1) { sprintf(sendbuffer, "BAD: unable to read link\n"); Log(LOG_LEVEL_ERR, "%s. (readlink: %s)", sendbuffer, GetErrorStr()); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } Log(LOG_LEVEL_DEBUG, "readlink '%s'", linkbuf); cfst.cf_readlink = linkbuf; } #endif /* !__MINGW32__ */ if ((!islink) && (stat(filename, &statbuf) == -1)) { Log(LOG_LEVEL_VERBOSE, "BAD: unable to stat file '%s'. (stat: %s)", filename, GetErrorStr()); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return -1; } Log(LOG_LEVEL_DEBUG, "Getting size of link deref '%s'", linkbuf); if (islink && (stat(filename, &statlinkbuf) != -1)) /* linktype=copy used by agent */ { statbuf.st_size = statlinkbuf.st_size; statbuf.st_mode = statlinkbuf.st_mode; statbuf.st_uid = statlinkbuf.st_uid; statbuf.st_gid = statlinkbuf.st_gid; statbuf.st_mtime = statlinkbuf.st_mtime; statbuf.st_ctime = statlinkbuf.st_ctime; } if (S_ISDIR(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_DIR; } if (S_ISREG(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_REGULAR; } if (S_ISSOCK(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_SOCK; } if (S_ISCHR(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_CHAR_; } if (S_ISBLK(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_BLOCK; } if (S_ISFIFO(statbuf.st_mode)) { cfst.cf_type = FILE_TYPE_FIFO; } cfst.cf_mode = statbuf.st_mode & 07777; cfst.cf_uid = statbuf.st_uid & 0xFFFFFFFF; cfst.cf_gid = statbuf.st_gid & 0xFFFFFFFF; cfst.cf_size = statbuf.st_size; cfst.cf_atime = statbuf.st_atime; cfst.cf_mtime = statbuf.st_mtime; cfst.cf_ctime = statbuf.st_ctime; cfst.cf_ino = statbuf.st_ino; cfst.cf_dev = statbuf.st_dev; cfst.cf_readlink = linkbuf; if (cfst.cf_nlink == CF_NOSIZE) { cfst.cf_nlink = statbuf.st_nlink; } #if !defined(__MINGW32__) if (statbuf.st_size > statbuf.st_blocks * DEV_BSIZE) #else # ifdef HAVE_ST_BLOCKS if (statbuf.st_size > statbuf.st_blocks * DEV_BSIZE) # else if (statbuf.st_size > ST_NBLOCKS(statbuf) * DEV_BSIZE) # endif #endif { cfst.cf_makeholes = 1; /* must have a hole to get checksum right */ } else { cfst.cf_makeholes = 0; } memset(sendbuffer, 0, CF_BUFSIZE); /* send as plain text */ Log(LOG_LEVEL_DEBUG, "OK: type = %d, mode = %" PRIoMAX ", lmode = %" PRIoMAX ", uid = %" PRIuMAX ", gid = %" PRIuMAX ", size = %" PRIdMAX ", atime=%" PRIdMAX ", mtime = %" PRIdMAX, cfst.cf_type, (uintmax_t)cfst.cf_mode, (uintmax_t)cfst.cf_lmode, (intmax_t)cfst.cf_uid, (intmax_t)cfst.cf_gid, (intmax_t) cfst.cf_size, (intmax_t) cfst.cf_atime, (intmax_t) cfst.cf_mtime); snprintf(sendbuffer, CF_BUFSIZE, "OK: %d %ju %ju %ju %ju %jd %jd %jd %jd %d %d %d %jd", cfst.cf_type, (uintmax_t)cfst.cf_mode, (uintmax_t)cfst.cf_lmode, (uintmax_t)cfst.cf_uid, (uintmax_t)cfst.cf_gid, (intmax_t)cfst.cf_size, (intmax_t) cfst.cf_atime, (intmax_t) cfst.cf_mtime, (intmax_t) cfst.cf_ctime, cfst.cf_makeholes, cfst.cf_ino, cfst.cf_nlink, (intmax_t) cfst.cf_dev); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); memset(sendbuffer, 0, CF_BUFSIZE); if (cfst.cf_readlink != NULL) { strcpy(sendbuffer, "OK:"); strcat(sendbuffer, cfst.cf_readlink); } else { sprintf(sendbuffer, "OK:"); } SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); return 0; }
static int copy_reg (const char *src_path, const char *dst_path, const struct cp_options *x, mode_t dst_mode, int *new_dst, struct stat const *src_sb) { char *buf; int buf_size; int dest_desc; int source_desc; struct stat sb; struct stat src_open_sb; char *cp; int *ip; int return_val = 0; off_t n_read_total = 0; int last_write_made_hole = 0; int make_holes = (x->sparse_mode == SPARSE_ALWAYS); source_desc = open (src_path, O_RDONLY); if (source_desc < 0) { error (0, errno, _("cannot open %s for reading"), quote (src_path)); return -1; } if (fstat (source_desc, &src_open_sb)) { error (0, errno, _("cannot fstat %s"), quote (src_path)); return_val = -1; goto close_src_desc; } /* Compare the source dev/ino from the open file to the incoming, saved ones obtained via a previous call to stat. */ if (! SAME_INODE (*src_sb, src_open_sb)) { error (0, 0, _("skipping file %s, as it was replaced while being copied"), quote (src_path)); return_val = -1; goto close_src_desc; } /* These semantics are required for cp. The if-block will be taken in move_mode. */ if (*new_dst) { dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode); } else { dest_desc = open (dst_path, O_WRONLY | O_TRUNC, dst_mode); if (dest_desc < 0 && x->unlink_dest_after_failed_open) { if (unlink (dst_path)) { error (0, errno, _("cannot remove %s"), quote (dst_path)); return_val = -1; goto close_src_desc; } /* Tell caller that the destination file was unlinked. */ *new_dst = 1; /* Try the open again, but this time with different flags. */ dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode); } } if (dest_desc < 0) { error (0, errno, _("cannot create regular file %s"), quote (dst_path)); return_val = -1; goto close_src_desc; } /* Determine the optimal buffer size. */ if (fstat (dest_desc, &sb)) { error (0, errno, _("cannot fstat %s"), quote (dst_path)); return_val = -1; goto close_src_and_dst_desc; } buf_size = ST_BLKSIZE (sb); #if HAVE_STRUCT_STAT_ST_BLOCKS if (x->sparse_mode == SPARSE_AUTO && S_ISREG (sb.st_mode)) { /* Use a heuristic to determine whether SRC_PATH contains any sparse blocks. */ if (fstat (source_desc, &sb)) { error (0, errno, _("cannot fstat %s"), quote (src_path)); return_val = -1; goto close_src_and_dst_desc; } /* If the file has fewer blocks than would normally be needed for a file of its size, then at least one of the blocks in the file is a hole. */ if (S_ISREG (sb.st_mode) && sb.st_size / ST_NBLOCKSIZE > ST_NBLOCKS (sb)) make_holes = 1; } #endif /* Make a buffer with space for a sentinel at the end. */ buf = (char *) alloca (buf_size + sizeof (int)); for (;;) { ssize_t n_read = read (source_desc, buf, buf_size); if (n_read < 0) { #ifdef EINTR if (errno == EINTR) continue; #endif error (0, errno, _("reading %s"), quote (src_path)); return_val = -1; goto close_src_and_dst_desc; } if (n_read == 0) break; n_read_total += n_read; ip = 0; if (make_holes) { buf[n_read] = 1; /* Sentinel to stop loop. */ /* Find first nonzero *word*, or the word with the sentinel. */ ip = (int *) buf; while (*ip++ == 0) ; /* Find the first nonzero *byte*, or the sentinel. */ cp = (char *) (ip - 1); while (*cp++ == 0) ; /* If we found the sentinel, the whole input block was zero, and we can make a hole. */ if (cp > buf + n_read) { /* Make a hole. */ if (lseek (dest_desc, (off_t) n_read, SEEK_CUR) < 0L) { error (0, errno, _("cannot lseek %s"), quote (dst_path)); return_val = -1; goto close_src_and_dst_desc; } last_write_made_hole = 1; } else /* Clear to indicate that a normal write is needed. */ ip = 0; } if (ip == 0) { size_t n = n_read; if (full_write (dest_desc, buf, n) != n) { error (0, errno, _("writing %s"), quote (dst_path)); return_val = -1; goto close_src_and_dst_desc; } last_write_made_hole = 0; } } /* If the file ends with a `hole', something needs to be written at the end. Otherwise the kernel would truncate the file at the end of the last write operation. */ if (last_write_made_hole) { #if HAVE_FTRUNCATE /* Write a null character and truncate it again. */ if (full_write (dest_desc, "", 1) != 1 || ftruncate (dest_desc, n_read_total) < 0) #else /* Seek backwards one character and write a null. */ if (lseek (dest_desc, (off_t) -1, SEEK_CUR) < 0L || full_write (dest_desc, "", 1) != 1) #endif { error (0, errno, _("writing %s"), quote (dst_path)); return_val = -1; } } close_src_and_dst_desc: if (close (dest_desc) < 0) { error (0, errno, _("closing %s"), quote (dst_path)); return_val = -1; } close_src_desc: if (close (source_desc) < 0) { error (0, errno, _("closing %s"), quote (src_path)); return_val = -1; } return return_val; }