diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2012-05-09 23:53:16 -0700 |
---|---|---|
committer | Jim Meyering <meyering@redhat.com> | 2012-05-10 11:02:42 +0200 |
commit | 9d308df13271a852aee7d46c65432fa84145ea31 (patch) | |
tree | 4973d91ba1d10e86875e8c2be8079dca131dad85 /src/truncate.c | |
parent | 2c436decf8bc57a9173c940a26c80358d499e1b6 (diff) | |
download | coreutils-9d308df13271a852aee7d46c65432fa84145ea31.tar.xz |
maint: handle file sizes more reliably
Problem reported by Samuel Thibault in <http://bugs.gnu.org/11424>.
* NEWS: Document this.
* src/dd.c (skip): Handle skipping past EOF on shared or typed
memory objects the same way as with regular files.
(dd_copy): It's OK to truncate shared memory objects.
* src/du.c (duinfo_add): Check for overflow.
(print_only_size): Report overflow.
(process_file): Ignore negative file sizes in the --apparent-size case.
* src/od.c (skip): Fix comment about st_size.
* src/split.c (main):
* src/truncate.c (do_ftruncate, main):
On files where st_size is not portable, fall back on using lseek
with SEEK_END to determine the size. Although strictly speaking
POSIX says the behavior is implementation-defined, in practice
if lseek returns a nonnegative value it's a reasonable one to
use for the file size.
* src/system.h (usable_st_size): Symlinks have reliable st_size too.
* tests/misc/truncate-dir-fail: Don't assume that getting the size
of a dir is not allowed, as it's now allowed on many platforms,
e.g., GNU/Linux.
Diffstat (limited to 'src/truncate.c')
-rw-r--r-- | src/truncate.c | 57 |
1 files changed, 41 insertions, 16 deletions
diff --git a/src/truncate.c b/src/truncate.c index 9b847d22a..e37ab3800 100644 --- a/src/truncate.c +++ b/src/truncate.c @@ -157,23 +157,36 @@ do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize, } if (rel_mode) { - uintmax_t const fsize = rsize < 0 ? sb.st_size : rsize; + uintmax_t fsize; - if (rsize < 0) /* fstat used above to get size. */ + if (0 <= rsize) + fsize = rsize; + else { - if (!S_ISREG (sb.st_mode) && !S_TYPEISSHM (&sb)) + off_t file_size; + if (usable_st_size (&sb)) { - error (0, 0, _("cannot get the size of %s"), quote (fname)); - return false; + file_size = sb.st_size; + if (file_size < 0) + { + /* Sanity check. Overflow is the only reason I can think + this would ever go negative. */ + error (0, 0, _("%s has unusable, apparently negative size"), + quote (fname)); + return false; + } } - if (sb.st_size < 0) + else { - /* Sanity check. Overflow is the only reason I can think - this would ever go negative. */ - error (0, 0, _("%s has unusable, apparently negative size"), - quote (fname)); - return false; + file_size = lseek (fd, 0, SEEK_END); + if (file_size < 0) + { + error (0, errno, _("cannot get the size of %s"), + quote (fname)); + return false; + } } + fsize = file_size; } if (rel_mode == rm_min) @@ -346,17 +359,29 @@ main (int argc, char **argv) if (ref_file) { - /* FIXME: Maybe support getting size of block devices. */ struct stat sb; + off_t file_size = -1; if (stat (ref_file, &sb) != 0) error (EXIT_FAILURE, errno, _("cannot stat %s"), quote (ref_file)); - if (!S_ISREG (sb.st_mode) && !S_TYPEISSHM (&sb)) - error (EXIT_FAILURE, 0, _("cannot get the size of %s"), + if (usable_st_size (&sb)) + file_size = sb.st_size; + else + { + int ref_fd = open (ref_file, O_RDONLY); + if (0 <= ref_fd) + { + off_t file_end = lseek (ref_fd, 0, SEEK_END); + if (0 <= file_end && close (ref_fd) == 0) + file_size = file_end; + } + } + if (file_size < 0) + error (EXIT_FAILURE, errno, _("cannot get the size of %s"), quote (ref_file)); if (!got_size) - size = sb.st_size; + size = file_size; else - rsize = sb.st_size; + rsize = file_size; } oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK; |