coreutils.git - GNU coreutils
| author | Paul Eggert <eggert@cs.ucla.edu> | 2021-05-01 15:19:16 -0700 |
|---|---|---|
| committer | Paul Eggert <eggert@cs.ucla.edu> | 2021-05-01 15:47:13 -0700 |
| commit | d435cfc0bc554b8baef2e690e138e27ac1b4d5b1 (patch) | |
| tree | 8716fceec4c63bc17831479f945d7e058986307d | |
| parent | 62a7ce5f503c4a7f8bb410f0cc10fefab106a4d2 (diff) | |
| download | coreutils-master.tar.gz | |
touch: fix wrong diagnostic (Bug#48106)HEADmaster
Problem reported by Roland (Bug#48106). * src/touch.c (touch): Take more care when deciding whether to use open_errno or utime_errno in the diagnostic. Stop worrying about SunOS 4 (which as part of the problem), as it’s long obsolete. For Solaris 10, verify that EINVAL really means the file was a directory.
| -rw-r--r-- | src/touch.c | 34 |
1 files changed, 19 insertions, 15 deletions
diff --git a/src/touch.c b/src/touch.c
index 653fd313b..46ddd86bb 100644
--- a/src/touch.c
+++ b/src/touch.c
@@ -122,7 +122,6 @@ get_reldate (struct timespec *result,
static bool
touch (char const *file)
{
- bool ok;
int fd = -1;
int open_errno = 0;
struct timespec const *t = newtime;
@@ -134,12 +133,7 @@ touch (char const *file)
/* Try to open FILE, creating it if necessary. */
fd = fd_reopen (STDIN_FILENO, file,
O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY, MODE_RW_UGO);
-
- /* Don't save a copy of errno if it's EISDIR, since that would lead
- touch to give a bogus diagnostic for e.g., 'touch /' (assuming
- we don't own / or have write access to it). On Solaris 5.6,
- and probably other systems, it is EINVAL. On SunOS4, it's EPERM. */
- if (fd == -1 && errno != EISDIR && errno != EINVAL && errno != EPERM)
+ if (fd < 0)
open_errno = errno;
}
@@ -162,9 +156,10 @@ touch (char const *file)
t = NULL;
}
- ok = (fdutimensat (fd, AT_FDCWD, (fd == STDOUT_FILENO ? NULL : file), t,
- (no_dereference && fd == -1) ? AT_SYMLINK_NOFOLLOW : 0)
- == 0);
+ char const *file_opt = fd == STDOUT_FILENO ? NULL : file;
+ int atflag = no_dereference ? AT_SYMLINK_NOFOLLOW : 0;
+ int utime_errno = (fdutimensat (fd, AT_FDCWD, file_opt, t, atflag) == 0
+ ? 0 : errno);
if (fd == STDIN_FILENO)
{
@@ -177,13 +172,22 @@ touch (char const *file)
else if (fd == STDOUT_FILENO)
{
/* Do not diagnose "touch -c - >&-". */
- if (!ok && errno == EBADF && no_create)
+ if (utime_errno == EBADF && no_create)
return true;
}
- if (!ok)
+ if (utime_errno != 0)
{
- if (open_errno)
+ /* Don't diagnose with open_errno if FILE is a directory, as that
+ would give a bogus diagnostic for e.g., 'touch /' (assuming we
+ don't own / or have write access). On Solaris 10 and probably
+ other systems, opening a directory like "." fails with EINVAL.
+ (On SunOS 4 it was EPERM but that's obsolete.) */
+ struct stat st;
+ if (open_errno
+ && ! (open_errno == EISDIR
+ || (open_errno == EINVAL
+ && stat (file, &st) == 0 && S_ISDIR (st.st_mode))))
{
/* The wording of this diagnostic should cover at least two cases:
- the file does not exist, but the parent directory is unwritable
@@ -193,9 +197,9 @@ touch (char const *file)
}
else
{
- if (no_create && errno == ENOENT)
+ if (no_create && utime_errno == ENOENT)
return true;
- error (0, errno, _("setting times of %s"), quoteaf (file));
+ error (0, utime_errno, _("setting times of %s"), quoteaf (file));
}
return false;
}