vfs: Rename vfs_emptydir() to vn_dir_check_empty()

No functional change.  While here, adapt comments to style(9).

Reviewed by:    kib
MFC after:      1 week
This commit is contained in:
Olivier Certner 2023-04-28 11:00:11 +02:00 committed by Konstantin Belousov
parent c21d87a88c
commit 2544b8e00c
4 changed files with 93 additions and 92 deletions

View File

@ -1160,7 +1160,7 @@ vfs_domount_first(
error = ENOTDIR;
}
if (error == 0 && (fsflags & MNT_EMPTYDIR) != 0)
error = vfs_emptydir(vp);
error = vn_dir_check_empty(vp);
if (error == 0) {
VI_LOCK(vp);
if ((vp->v_iflag & VI_MOUNT) == 0 && vp->v_mountedhere == NULL)

View File

@ -6382,96 +6382,6 @@ filt_vfsvnode(struct knote *kn, long hint)
return (res);
}
/*
* Returns whether the directory is empty or not.
* If it is empty, the return value is 0; otherwise
* the return value is an error value (which may
* be ENOTEMPTY).
*/
int
vfs_emptydir(struct vnode *vp)
{
struct thread *const td = curthread;
char *dirbuf;
size_t dirbuflen, len;
off_t off;
int eofflag, error;
struct dirent *dp;
struct vattr va;
ASSERT_VOP_LOCKED(vp, "vfs_emptydir");
VNPASS(vp->v_type == VDIR, vp);
error = VOP_GETATTR(vp, &va, td->td_ucred);
if (error != 0)
return (error);
dirbuflen = max(DEV_BSIZE, GENERIC_MAXDIRSIZ);
if (dirbuflen < va.va_blocksize)
dirbuflen = va.va_blocksize;
dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
len = 0;
off = 0;
eofflag = 0;
for (;;) {
error = vn_dir_next_dirent(vp, td, dirbuf, dirbuflen,
&dp, &len, &off, &eofflag);
if (error != 0)
goto end;
if (len == 0) {
/* EOF */
error = 0;
goto end;
}
/*
* Skip whiteouts. Unionfs operates on filesystems only and not
* on hierarchies, so these whiteouts would be shadowed on the
* system hierarchy but not for a union using the filesystem of
* their directories as the upper layer. Additionally, unionfs
* currently transparently exposes union-specific metadata of
* its upper layer, meaning that whiteouts can be seen through
* the union view in empty directories. Taking into account
* these whiteouts would then prevent mounting another
* filesystem on such effectively empty directories.
*/
if (dp->d_type == DT_WHT)
continue;
/*
* Any file in the directory which is not '.' or '..' indicates
* the directory is not empty.
*/
switch (dp->d_namlen) {
case 2:
if (dp->d_name[1] != '.') {
/* Can't be '..' (nor '.') */
error = ENOTEMPTY;
goto end;
}
/* FALLTHROUGH */
case 1:
if (dp->d_name[0] != '.') {
/* Can't be '..' nor '.' */
error = ENOTEMPTY;
goto end;
}
break;
default:
error = ENOTEMPTY;
goto end;
}
}
end:
free(dirbuf, M_TEMP);
return (error);
}
int
vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off)
{

View File

@ -3897,6 +3897,97 @@ vn_dir_next_dirent(struct vnode *vp, struct thread *td,
return (error);
}
/*
* Checks whether a directory is empty or not.
*
* If the directory is empty, returns 0, and if it is not, ENOTEMPTY. Other
* values are genuine errors preventing the check.
*/
int
vn_dir_check_empty(struct vnode *vp)
{
struct thread *const td = curthread;
char *dirbuf;
size_t dirbuflen, len;
off_t off;
int eofflag, error;
struct dirent *dp;
struct vattr va;
ASSERT_VOP_LOCKED(vp, "vfs_emptydir");
VNPASS(vp->v_type == VDIR, vp);
error = VOP_GETATTR(vp, &va, td->td_ucred);
if (error != 0)
return (error);
dirbuflen = max(DEV_BSIZE, GENERIC_MAXDIRSIZ);
if (dirbuflen < va.va_blocksize)
dirbuflen = va.va_blocksize;
dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
len = 0;
off = 0;
eofflag = 0;
for (;;) {
error = vn_dir_next_dirent(vp, td, dirbuf, dirbuflen,
&dp, &len, &off, &eofflag);
if (error != 0)
goto end;
if (len == 0) {
/* EOF */
error = 0;
goto end;
}
/*
* Skip whiteouts. Unionfs operates on filesystems only and
* not on hierarchies, so these whiteouts would be shadowed on
* the system hierarchy but not for a union using the
* filesystem of their directories as the upper layer.
* Additionally, unionfs currently transparently exposes
* union-specific metadata of its upper layer, meaning that
* whiteouts can be seen through the union view in empty
* directories. Taking into account these whiteouts would then
* prevent mounting another filesystem on such effectively
* empty directories.
*/
if (dp->d_type == DT_WHT)
continue;
/*
* Any file in the directory which is not '.' or '..' indicates
* the directory is not empty.
*/
switch (dp->d_namlen) {
case 2:
if (dp->d_name[1] != '.') {
/* Can't be '..' (nor '.') */
error = ENOTEMPTY;
goto end;
}
/* FALLTHROUGH */
case 1:
if (dp->d_name[0] != '.') {
/* Can't be '..' nor '.' */
error = ENOTEMPTY;
goto end;
}
break;
default:
error = ENOTEMPTY;
goto end;
}
}
end:
free(dirbuf, M_TEMP);
return (error);
}
static u_long vn_lock_pair_pause_cnt;
SYSCTL_ULONG(_debug, OID_AUTO, vn_lock_pair_pause, CTLFLAG_RD,

View File

@ -1102,8 +1102,8 @@ struct dirent;
int vn_dir_next_dirent(struct vnode *vp, struct thread *td,
char *dirbuf, size_t dirbuflen,
struct dirent **dpp, size_t *len, off_t *off, int *eofflag);
int vn_dir_check_empty(struct vnode *vp);
int vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off);
int vfs_emptydir(struct vnode *vp);
int vfs_unixify_accmode(accmode_t *accmode);