Skip to contentSkip to conversation

unlink(nonexistent): EROFS or ENOENT?

PATCH New Michael Tokarev 2 participants 7 messages May 29, 2011 – Jun 6, 2011 VFS

lore
Share & tools
lore
#0 lore raw

Hello.

Just noticed that at least on ext4, unlinking a non-existing file from a read-only filesystem results in EROFS instead of ENOENT. I'd expect it return ENOENT - it is more logical, at least in my opinion.

For one, (readonly) NFS mount returns ENOENT in this case.

Thanks!

/mjt

#1 lore raw

29.05.2011 20:08, Michael Tokarev пишет:

Quoted 7 lines

Hello.

Just noticed that at least on ext4, unlinking a non-existing file from a read-only filesystem results in EROFS instead of ENOENT. I'd expect it return ENOENT - it is more logical, at least in my opinion.

http://pubs.opengroup.org/onlinepubs/009695399/functions/unlink.html
this case is quite clear:

[EROFS]
The directory entry to be unlinked
is part of a read-only file system

Ie, the entry is a _part_ of a file system, so it should be _existing_ entry to start with.

Quoted 4 lines

For one, (readonly) NFS mount returns ENOENT in this case.

Thanks!

/mjt

#2 lore raw
In reply to Michael Tokarev
Quoted 10 lines · Michael Tokarev

Hello.

Just noticed that at least on ext4, unlinking a non-existing file from a read-only filesystem results in EROFS instead of ENOENT. I'd expect it return ENOENT - it is more logical, at least in my opinion.

For one, (readonly) NFS mount returns ENOENT in this case.

Um, it doesn't for me. Testing on v3.0-rc1:

# ls /test/foo; rm /test/foo
ls: cannot access /test/foo: No such file or directory
rm: cannot remove `/test/foo': No such file or directory
# ls /test/null; rm /test/null
/test/null
rm: cannot remove `/test/null': Read-only file system
# grep test /proc/mounts
/dev/vdb /test ext4 ro,relatime,user_xattr,barrier=1,data=ordered 0 0

- Ted

#3 lore raw

Thank you for the answer. I thought noone will reply... ;)

Quoted 9 lines · Ted Ts'o
Quoted 8 lines · Michael Tokarev

Hello.

Just noticed that at least on ext4, unlinking a non-existing file from a read-only filesystem results in EROFS instead of ENOENT. I'd expect it return ENOENT - it is more logical, at least in my opinion.

Quoted 8 lines
Quoted 2 lines

For one, (readonly) NFS mount returns ENOENT in this case.

Um, it doesn't for me. Testing on v3.0-rc1:

# ls /test/foo; rm /test/foo
ls: cannot access /test/foo: No such file or directory
rm: cannot remove `/test/foo': No such file or directory

This is a hack in coreutils rm to work around this kernel change. The comment at
http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/remove.c#n450
says:

/* The unlinkat from kernels like linux-2.6.32 reports EROFS even for
nonexistent files. When the file is indeed missing, map that to ENOENT, so that rm -f ignores it, as required. Even without -f, this is useful because it makes rm print the more precise diagnostic. */

so that rm(1) calls stat(2) to see if the file actually exist if unlinkat() returned EROFS, and turns this errno into ENOENT.

That is, rm(1) output is not a good indicator. Use

strace rm -f /test/foo 2>&1 | grep unlink

to see the actual errno reported by the kernel.

Here's the POSIX description of unlink (and unlinkat) again:
http://pubs.opengroup.org/onlinepubs/009695399/functions/unlink.html

Thanks!

/mjt

#4 lore raw
Quoted 26 lines · Michael Tokarev

Thank you for the answer. I thought noone will reply... ;)

Quoted 9 lines · Ted Ts'o
Quoted 8 lines · Michael Tokarev

Hello.

Just noticed that at least on ext4, unlinking a non-existing file from a read-only filesystem results in EROFS instead of ENOENT. I'd expect it return ENOENT - it is more logical, at least in my opinion.

Quoted 8 lines
Quoted 2 lines

For one, (readonly) NFS mount returns ENOENT in this case.

Um, it doesn't for me. Testing on v3.0-rc1:

# ls /test/foo; rm /test/foo
ls: cannot access /test/foo: No such file or directory
rm: cannot remove `/test/foo': No such file or directory

This is a hack in coreutils rm to work around this kernel change. The comment at
http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/remove.c#n450
says:

Quoted 8 lines

/* The unlinkat from kernels like linux-2.6.32 reports EROFS even for
nonexistent files. When the file is indeed missing, map that to ENOENT, so that rm -f ignores it, as required. Even without -f, this is useful because it makes rm print the more precise diagnostic. */

so that rm(1) calls stat(2) to see if the file actually exist if unlinkat() returned EROFS, and turns this errno into ENOENT.

And another followup to this, -- the original case when I actually
noticed the problem. A readonly-mounted root filesystem with /etc
in git (the repository is in /var, symlinked from /etc/.git). I deleted a few files from /etc (when it was readwrite), and noticed that I forgot to commit the change. So I used `git rm oldfiles' and voila, git, for the first time, refused to commit stuff for me in this configuration, -- before, I was always able to _commit_ the changes even if the working tree is read-only. It works for everything but not for unlinks.

Quoted 17 lines

That is, rm(1) output is not a good indicator. Use

strace rm -f /test/foo 2>&1 | grep unlink

to see the actual errno reported by the kernel.

Here's the POSIX description of unlink (and unlinkat) again:
http://pubs.opengroup.org/onlinepubs/009695399/functions/unlink.html

Thanks!

/mjt
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#5 lore raw
In reply to Michael Tokarev
Quoted 8 lines · Michael Tokarev

Thank you for the answer. I thought noone will reply... ;)

Quoted 5 lines
Quoted 5 lines

Just noticed that at least on ext4, unlinking a non-existing file from a read-only filesystem results in EROFS instead of ENOENT. I'd expect it return ENOENT - it is more logical, at least in my opinion.

Quoted 4 lines

/* The unlinkat from kernels like linux-2.6.32 reports EROFS even for
nonexistent files. When the file is indeed missing, map that to ENOENT, so that rm -f ignores it, as required. Even without -f, this is useful because it makes rm print the more precise diagnostic. */

OK, I see what's going on. This check is in the VFS layer, so it affects all filesystems; it's not an ext4-specific thing.

Patch coming shortly.

- Ted

#6 lore raw

PATCH 1/1 vfs: make unlink() return ENOENT in preference to EROFS

If user space attempts to unlink a non-existent file, and the file system is mounted read-only, return ENOENT instead of EROFS. Either error code is arguably valid/correct, but ENOENT is a more specific error message.

---
 fs/namei.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
fs/namei.c +2 -2
index 1ab641f..a9edbe0 100644
@@ -2708,9 +2708,9 @@ static long do_unlinkat(int dfd, const char __user *pathname)
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
 		/* Why not before? Because we want correct error value */
-		if (nd.last.name[nd.last.len])
-			goto slashes;
 		inode = dentry->d_inode;
+		if (nd.last.name[nd.last.len] || !inode)
+			goto slashes;
 		if (inode)
 			ihold(inode);
 		error = mnt_want_write(nd.path.mnt);
Signature · 1 line
1.7.4.1.22.gec8e1.dirty