The following excerpt from this essay potentially explains why that directory refuses to be deleted:
NFSv4 requires that all filenames be exchanged using UTF-8 over the wire. The NFSv4 specification, RFC 3530, says that filenames should be UTF-8 encoded in section 1.4.3: “In a slight departure, file and directory names are encoded with UTF-8 to deal with the basics of internationalization.” The same text is also found in the newer NFS 4.1 RFC (RFC 5661) section 1.7.3. The current Linux NFS client simply passes filenames straight through, without any conversion from the current locale to and from UTF-8. Using non-UTF-8 filenames could be a real problem on a system using a remote NFSv4 system; any NFS server that follows the NFS specification is supposed to reject non-UTF-8 filenames. So if you want to ensure that your files can actually be stored from a Linux client to an NFS server, you must currently use UTF-8 filenames. In other words, although some people think that Linux doesn’t force a particular character encoding on filenames, in practice it already requires UTF-8 encoding for filenames in certain cases.
UTF-8 is a longer-term approach. Systems have to support UTF-8 as well as the many older encodings, giving people time to switch to UTF-8. To use “UTF-8 everywhere”, all tools need to be updated to support UTF-8. Years ago, this was a big problem, but as of 2011 this is essentially a solved problem, and I think the trajectory is very clear for those few trailing systems.
Not all byte sequences are legal UTF-8, and you don’t want to have to figure out how to display them. If the kernel enforces these restrictions, ensuring that only UTF-8 filenames are allowed, then there’s no problem... all the filenames will be legal UTF-8. Markus Kuhn’s utf8_check C function can quickly determine if a sequence is valid UTF-8.
The filesystem should be requiring that filenames meet some standard, not because of some evil need to control people, but simply so that the names can always be displayed correctly at a later time. The lack of standards makes things harder for users, not easier. Yet the filesystem doesn’t force filenames to be UTF-8, so it can easily have garbage.
The members of findutils
aware of it, it's for compatible with *BSD:
One of the reasons that we skip deletion of "." is for compatibility
with *BSD, where this action originated.
The NEWS in findutils source code shows that they decided to keep the behavior:
#20802: If -delete fails, find's exit status will now be non-zero. However, find still skips trying to delete ".".
[UPDATE]
Since this question become one of the hot topic, so i dive into FreeBSD source code and come out a more convincing reason.
Let's see the find utility source code of FreeBSD:
int
f_delete(PLAN *plan __unused, FTSENT *entry)
{
/* ignore these from fts */
if (strcmp(entry->fts_accpath, ".") == 0 ||
strcmp(entry->fts_accpath, "..") == 0)
return 1;
...
/* rmdir directories, unlink everything else */
if (S_ISDIR(entry->fts_statp->st_mode)) {
if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
warn("-delete: rmdir(%s)", entry->fts_path);
} else {
if (unlink(entry->fts_accpath) < 0)
warn("-delete: unlink(%s)", entry->fts_path);
}
...
As you can see, if it doesn't filter out dot and dot-dot, then it will reach rmdir()
C function defined by POSIX's unistd.h
.
Do a simple test, rmdir with dot/dot-dot argument will return -1:
printf("%d\n", rmdir(".."));
Let's take a look how POSIX describe rmdir:
If the path argument refers to a path whose final component is either
dot or dot-dot, rmdir() shall fail.
No reason was given why shall fail
.
I found rename
explain some reason:
Renaming dot or dot-dot is prohibited in order to prevent cyclical
file system paths.
Cyclical file system paths ?
I look over The C Programming Language (2nd Edition) and search for directory topic, surprisingly i found the code is similar:
if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
continue;
And the comment !
Each directory always contains entries for itself, called ".", and its
parent, ".."; these must be skipped, or the program will loop forever.
"loop forever", this is same like how rename
describe it as "cyclical file system paths" above.
I slightly modify the code and to make it run in Kali Linux based on this answer:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
void fsize(char *);
void dirwalk(char *, void (*fcn)(char *));
int
main(int argc, char **argv) {
if (argc == 1)
fsize(".");
else
while (--argc > 0) {
printf("start\n");
fsize(*++argv);
}
return 0;
}
void fsize(char *name) {
struct stat stbuf;
if (stat(name, &stbuf) == -1 ) {
fprintf(stderr, "fsize: can't access %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
printf("%81d %s\n", stbuf.st_size, name);
}
#define MAX_PATH 1024
void dirwalk(char *dir, void (*fcn)(char *))
{
char name[MAX_PATH];
struct dirent *dp;
DIR *dfd;
if ((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
sleep(1);
printf("d_name: S%sG\n", dp->d_name);
if (strcmp(dp->d_name, ".") == 0
|| strcmp(dp->d_name, "..") == 0) {
printf("hole dot\n");
continue;
}
if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name)) {
printf("mocha\n");
fprintf(stderr, "dirwalk: name %s/%s too long\n",
dir, dp->d_name);
}
else {
printf("ice\n");
(*fcn)(dp->d_name);
}
}
closedir(dfd);
}
Let's see:
xb@dnxb:/test/dot$ ls -la
total 8
drwxr-xr-x 2 xiaobai xiaobai 4096 Nov 20 04:14 .
drwxr-xr-x 3 xiaobai xiaobai 4096 Nov 20 04:14 ..
xb@dnxb:/test/dot$
xb@dnxb:/test/dot$ cc /tmp/kr/fsize.c -o /tmp/kr/a.out
xb@dnxb:/test/dot$ /tmp/kr/a.out .
start
d_name: S..G
hole dot
d_name: S.G
hole dot
4096 .
xb@dnxb:/test/dot$
It work correctly, now what if I comment out the continue
instruction:
xb@dnxb:/test/dot$ cc /tmp/kr/fsize.c -o /tmp/kr/a.out
xb@dnxb:/test/dot$ /tmp/kr/a.out .
start
d_name: S..G
hole dot
ice
d_name: S..G
hole dot
ice
d_name: S..G
hole dot
ice
^C
xb@dnxb:/test/dot$
As you can see, I have to use Ctrl+C to kill this infinitely loop program.
The '..' directory read its first entry '..' and loop forever.
Conclusion:
GNU findutils
try to compatible with find
utility in *BSD.
find
utility in *BSD internally use rmdir
POSIX-compliant C function which dot/dot-dot is not allow.
The reason of rmdir
do not allow dot/dot-dot is prevent cyclical file system paths.
The C Programming Language written by K&R shows the example of how dot/dot-dot will lead to forever loop program.
Best Answer
If you are trying to delete a directory
foo/bar/
, the permissions ofbar
isn't the relevant factor. Removing the namebar
from directoryfoo
is a modification offoo
. So you need write permissions onfoo
.In your case, check the current directory's permissions with
ls -ld .
You might find this answer to "why is rm allowed to delete a file under ownership of a different user?" enlightening.