Hi Quentin, On Sun, Oct 01, 2023 at 03:57:19PM +0100, Quentin Armitage wrote: > truncate(2) returns EINVAL if the file argument is a socket, a FIFO or > a character or block device. The current man page indicates that > ftruncate() returns EINVAL for an fd that does not reference a regular > file, but for truncate() the only reason given for returning EINVAL is > that the length is invalid. > > The following test program demonstrates the errors returned by truncate(): > ===================================================== I tweaked the program a little bit. > #define _GNU_SOURCE > > #include <unistd.h> > #include <stdio.h> > #include <stdlib.h> > #include <errno.h> > #include <sys/types.h> > #include <fcntl.h> > #include <sys/stat.h> > #include <sys/sysmacros.h> > #include <string.h> > #include <err.h> Add a space before the '#', so that git doesn't mess with it so easily. Also, sort(1). > > struct { > const char *fname; Use 4-space indent. > mode_t mode; > unsigned dev_maj; > unsigned dev_min; > } nodes[] = { > { "/tmp/trunc_file", S_IFREG | 0666 }, Explicitly set ,0 ,0 > { "/tmp/trunc_fifo", S_IFIFO | 0666 }, > { "/tmp/trunc_socket", S_IFSOCK | 0666 }, > { "/tmp/trunc_char_dev", S_IFCHR | 0666, 10, 7}, // Second Amiga mouse, /dev/amigamouse1 > { "/tmp/trunc_blk_dev", S_IFBLK | 0666, 13, 3 }, // Was XT disk /dev/xd3 > { "/tmp/trunc_dir", 0666 }, > }; > > int main(int C, char **V) int main(void) is legal ISO C. Since you're not using them, you can just use void. > { > int n; > int ret; > > for (n = 0; n < sizeof(nodes) / sizeof(nodes[0]); n++) { We prefer C99 variables for loops (defined in the for line). Also, 'n' should be of type size_t. > /* Create the nodes */ > if (!(nodes[n].mode & S_IFMT)) > ret = mkdir(nodes[n].fname, nodes[n].mode); > else > ret = mknod(nodes[n].fname, nodes[n].mode, > makedev(nodes[n].dev_maj, nodes[n].dev_min)); > > if (ret) { > warn("mknod(%s) errno %d", nodes[n].fname, errno); With a recent-enough glibc, the following is a bit simpler: warn("mknod(\"%s\"): %#m", nodes[n].fname); > continue; > } > > /* Returns EINVAL for IFSOCK, IFIFO, S_IFBLK, S_IFCHR, EISDIR for a directory */ > ret = truncate(nodes[n].fname, 0); > > if (ret) > warn("truncate(\"%s\") failed with errno %s", nodes[n].fname, > strerrorname_np(errno)); For consistency, the same goes here: warnc(ret == -1 ? errno : 0, "truncate(\"%s\"): %#m", nodes[n].fname); (I also used stderr for success, since it's still an error report; it just says there are no errors.) warnc(3) is available via libbsd (glibc could catch up here). > else > printf("truncate(\"%s\") succeeded\n", nodes[n].fname); > > /* Remove the nodes */ > if (!(nodes[n].mode & S_IFMT)) > ret = rmdir(nodes[n].fname); > else > ret = unlink(nodes[n].fname); > if (ret) > warn("unlink(%s) errno %d", nodes[n].fname, errno); > } > } > ===================================================== > > Compile the program and run it as user root. > > output (if program name is trunc) should be: > truncate("/tmp/trunc_file") succeeded > trunc: truncate("/tmp/trunc_fifo") failed with errno EINVAL: Invalid argument > trunc: truncate("/tmp/trunc_socket") failed with errno EINVAL: Invalid argument > trunc: truncate("/tmp/trunc_char_dev") failed with errno EINVAL: Invalid argument > trunc: truncate("/tmp/trunc_blk_dev") failed with errno EINVAL: Invalid argument > trunc: truncate("/tmp/trunc_dir") failed with errno EISDIR: Is a directory Here's what I tweaked, if you want to just pick it: ===================================================== #define _GNU_SOURCE #include <err.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/sysmacros.h> #include <sys/types.h> #include <unistd.h> struct { const char *fname; mode_t mode; unsigned dev_maj; unsigned dev_min; } nodes[] = { {"/tmp/trunc_file", S_IFREG | 0666, 0, 0}, {"/tmp/trunc_fifo", S_IFIFO | 0666, 0, 0}, {"/tmp/trunc_socket", S_IFSOCK | 0666, 0, 0}, {"/tmp/trunc_char_dev", S_IFCHR | 0666, 10, 7}, // Second Amiga mouse, /dev/amigamouse1 {"/tmp/trunc_blk_dev", S_IFBLK | 0666, 13, 3}, // Was XT disk /dev/xd3 {"/tmp/trunc_dir", 0666, 0, 0}, }; int main(void) { int ret; for (size_t n = 0; n < sizeof(nodes) / sizeof(nodes[0]); n++) { /* Create the nodes */ if (!(nodes[n].mode & S_IFMT)) ret = mkdir(nodes[n].fname, nodes[n].mode); else ret = mknod(nodes[n].fname, nodes[n].mode, makedev(nodes[n].dev_maj, nodes[n].dev_min)); if (ret == -1) { warn("mknod(\"%s\") %#m", nodes[n].fname); continue; } /* Returns EINVAL for IFSOCK, IFIFO, S_IFBLK, S_IFCHR, EISDIR for a directory */ ret = truncate(nodes[n].fname, 0); warnc(ret == -1 ? errno : 0, "truncate(\"%s\"): %#m", nodes[n].fname); /* Remove the nodes */ ret = (nodes[n].mode & S_IFMT) ? unlink(nodes[n].fname) : rmdir(nodes[n].fname); if (ret == -1) warn("unlink(\"%s\"): %#m", nodes[n].fname); } } ===================================================== Compile the program with $(pkgconf --cflags --libs libbsd-overlay) Run it as user root. The output (if program name is trunc) should be: trunc: truncate("/tmp/trunc_file"): 0: Success trunc: truncate("/tmp/trunc_fifo"): EINVAL: Invalid argument trunc: truncate("/tmp/trunc_socket"): EINVAL: Invalid argument trunc: truncate("/tmp/trunc_char_dev"): EINVAL: Invalid argument trunc: truncate("/tmp/trunc_blk_dev"): EINVAL: Invalid argument trunc: truncate("/tmp/trunc_dir"): EISDIR: Is a directory > > Signed-off-by: Quentin Armitage <quentin@xxxxxxxxxxxxxxx> > > diff --git a/man2/truncate.2 b/man2/truncate.2 > index 703f598b3..44750b9e2 100644 > --- a/man2/truncate.2 > +++ b/man2/truncate.2 > @@ -112,7 +112,9 @@ and > .B EINVAL > The argument > .I length > -is negative or larger than the maximum file size. > +is negative or larger than the maximum file size, > +or the named file is a socket, a FIFO, > +or a block or character device. The same page has the following below, for ftruncate(2): EINVAL fd does not reference a regular file or a POSIX shared memory object. You could check that to have consistent wording in both. Cheers, Alex > .TP > .B EIO > An I/O error occurred updating the inode. >
Attachment:
signature.asc
Description: PGP signature