* [patch v2] truncate.2: EINVAL is returned for non regular files except directories
@ 2023-10-01 14:57 Quentin Armitage
2023-10-03 0:17 ` Alejandro Colomar
0 siblings, 1 reply; 2+ messages in thread
From: Quentin Armitage @ 2023-10-01 14:57 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: linux-man
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():
=====================================================
#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>
struct {
const char *fname;
mode_t mode;
unsigned dev_maj;
unsigned dev_min;
} nodes[] = {
{ "/tmp/trunc_file", S_IFREG | 0666 },
{ "/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 n;
int ret;
for (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) {
warn("mknod(%s) errno %d", nodes[n].fname, errno);
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));
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
Signed-off-by: Quentin Armitage <quentin@armitage.org.uk>
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.
.TP
.B EIO
An I/O error occurred updating the inode.
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [patch v2] truncate.2: EINVAL is returned for non regular files except directories
2023-10-01 14:57 [patch v2] truncate.2: EINVAL is returned for non regular files except directories Quentin Armitage
@ 2023-10-03 0:17 ` Alejandro Colomar
0 siblings, 0 replies; 2+ messages in thread
From: Alejandro Colomar @ 2023-10-03 0:17 UTC (permalink / raw)
To: Quentin Armitage; +Cc: linux-man
[-- Attachment #1: Type: text/plain, Size: 6906 bytes --]
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@armitage.org.uk>
>
> 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 #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2023-10-03 0:17 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-10-01 14:57 [patch v2] truncate.2: EINVAL is returned for non regular files except directories Quentin Armitage
2023-10-03 0:17 ` Alejandro Colomar
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox