NAME
genfs_rename
,
genfs_insane_rename
,
genfs_sane_rename
—
generic framework for implementing
VOP_RENAME(9)
SYNOPSIS
int
genfs_insane_rename
(struct
vop_rename_args *v, int (*sane_rename)(struct vnode
*fdvp, struct componentname *fcnp, struct vnode *tdvp, struct componentname
*tcnp, kauth_cred_t, bool));
int
genfs_sane_rename
(const struct
genfs_rename_ops *gro, struct vnode *fdvp,
struct componentname *fcnp, void
*fde, struct vnode *tdvp, struct
componentname *tcnp, void *tde,
kauth_cred_t cred, bool
posixly_correct);
int
genfs_rename_knote
(struct vnode
*fdvp, struct vnode *fvp, struct
vnode *tdvp, struct vnode *tvp);
void
genfs_rename_cache_purge
(struct vnode
*fdvp, struct vnode *fvp, struct
vnode *tdvp, struct vnode *tvp);
int
genfs_ufslike_rename_check_possible
(unsigned
long fdflags, unsigned long fflags,
unsigned long tdflags, unsigned long
tflags, bool clobber, unsigned
long immutable, unsigned long append);
int
genfs_ufslike_rename_check_permitted
(kauth_cred_t
cred, struct vnode *fdvp, mode_t
fdmode, uid_t fduid, struct
vnode *fvp, uid_t fuid, struct
vnode *tdvp, mode_t tdmode,
uid_t tduid, struct vnode *tvp,
uid_t tuid);
int
genfs_ufslike_remove_check_possible
(unsigned
long dflags, unsigned long flags,
unsigned long immutable, unsigned long
append);
int
genfs_ufslike_remove_check_permitted
(kauth_cred_t
cred, struct vnode *dvp, mode_t
dmode, uid_t duid, struct vnode
*vp, uid_t uid);
DESCRIPTION
Thegenfs_rename
functions provide a
file-system-independent framework for implementing
VOP_RENAME(9) with correct locking and error-checking.
Implementing rename is nontrivial. If you are
doing it for a new file system, you should consider starting from
tmpfs_rename
()
as implemented in sys/fs/tmpfs/tmpfs_rename.c and
adapting it to your file system's physical operations.
Because there are so many moving parts to a rename operation,
genfs_rename
uses the following naming
conventions:
- mp (mount point)
- mount point of the file system in question
- fdvp (from directory vnode pointer)
- directory from which we are removing an entry
- fcnp (from componentname pointer)
- name of entry to remove from fdvp
- fde (from directory entry)
- fs-specific data about the entry in fdvp
- fvp (from vnode pointer)
- file at the entry named fcnp in fdvp
- tdvp (to directory vnode pointer)
- directory to which we are adding an entry
- tcnp (to componentname pointer)
- name of entry to add to tdvp
- tde (to directory entry)
- fs-specific data about the entry in tdvp
- tvp (to vnode pointer)
- file previously at the entry named tcnp in
tdvp, to be replaced, if any, or
NULL
if there was no entry before - vp (vnode pointer)
- any file
- dvp (directory vnode pointer)
- any directory with an entry for vp
A file system mumblefs should implement
various file-system-dependent parts of the rename operation in a
struct genfs_rename_ops, and use
genfs_rename
to implement
mumblefs_rename
()
for VOP_RENAME(9) as follows:
static const struct genfs_rename_ops mumblefs_genfs_rename_ops; static int mumblefs_sane_rename( struct vnode *fdvp, struct componentname *fcnp, struct vnode *tdvp, struct componentname *tcnp, kauth_cred_t cred, bool posixly_correct) { struct mumblefs_lookup_results fulr, tulr; return genfs_sane_rename(&mumblefs_genfs_rename_ops, fdvp, fcnp, &fulr, tdvp, tcnp, &tulr, cred, posixly_correct); } int mumblefs_rename(void *v) { return genfs_insane_rename(v, &mumblefs_sane_rename); }
The split between
mumblefs_rename
()
and
mumblefs_sane_rename
()
is designed to enable us to easily change the
VOP_RENAME(9) interface, which is currently designed for a
broken (hence ‘insane’) locking scheme, to a more sensible
locking scheme once all the file systems have their rename operations split
up thus.
The struct mumblefs_lookup_results structure is storage for information about directory entries which needs to pass from the lookups of the children (see the gro_lookup member of struct genfs_rename_ops) to the physical on-disk rename or remove operations (see the gro_rename and gro_remove members of struct genfs_rename_ops).
Callers must implement the following operations as members in a
struct genfs_rename_ops structure passed to
genfs_rename
:
- int
(*gro_genealogy)
(struct mount *mp, kauth_cred_t cred, struct vnode *fdvp, struct vnode *tdvp, struct vnode **intermediate_node_ret) - Walk up the directory tree from the directory vnode
tdvp until hitting either fdvp
or the root. If fdvp is hit, store the child of
fdvp through which the path from
tdvp passed in
*intermediate_node_ret, referenced but unlocked. If
fdvp is not hit, store
NULL
in *intermediate_node_ret. Return zero on success or error on failure. (Failure means file-system-specific failures, not hitting or missing fdvp.)fdvp and tdvp are guaranteed to be distinct, nonnull, referenced, and unlocked. Since no locks are held on entry except for the file-system-wide rename lock, gro_genealogy may take any locks it pleases.
- int
(*gro_lock_directory)
(struct mount *mp, struct vnode *vp) - Lock the directory vnode vp, but fail if it has been rmdired already. Return zero on success or error on failure.
- int
(*gro_lookup)
(struct mount *mp, struct vnode *dvp, struct componentname *cnp, void *de, struct vnode **vpp) - Look up the entry in dvp for
cnp, storing the vnode in *vpp
and using de, one of the pointers passed to
genfs_sane_rename
, to store information about the directory entry as needed by the file system's gro_rename operation, and return zero. If there is no such entry, return error.dvp is guaranteed to be locked, and the vnode returned in *vpp must be unlocked. However, gro_lookup may temporarily lock the vnode without causing deadlock.
- bool
(*gro_directory_empty_p)
(struct mount *mp, kauth_cred_t cred, struct vnode *vp, struct vnode *dvp) - Return true if the directory vnode vp is empty. The
argument dvp is the parent of
vp, as required for this check by some file systems.
dvp and vp are guaranteed to be distinct, nonnull, referenced, and locked.
- int
(*gro_rename_check_possible)
(struct mount *mp, struct vnode *fdvp, struct vnode *fvp, struct vnode *tdvp, struct vnode *tvp) - Return zero if the file system might allow the rename independent of
credentials, or error if not. This should check, for example, any
immutability flags in the vnodes in question, and should use
genfs_ufslike_rename_check_possible
() for file systems similar to UFS/FFS.fdvp and tdvp may be the same; every other pair of vnodes is guaranteed to be distinct. tvp may be
NULL
; every other vnode is guaranteed to be nonnull. All three or four vnodes are guaranteed to be referenced and locked. - int
(*gro_rename_check_permitted)
(struct mount *mp, kauth_cred_t cred, struct vnode *fdvp, struct vnode *fvp, struct vnode *tdvp, struct vnode *tvp) - Return zero if the file system allows the rename given the credentials
cred, or error if not. This should check, for
example, the ownership and permissions bits of the vnodes in question, and
should use
genfs_ufslike_rename_check_permitted
() for file systems similar to UFS/FFS.fdvp and tdvp may be the same; every other pair of vnodes is guaranteed to be distinct. tvp may be
NULL
; every other vnode is guaranteed to be nonnull. All three or four vnodes are guaranteed to be referenced and locked. - int
(*gro_rename)
(struct mount *mp, kauth_cred_t cred, struct vnode *fdvp, struct componentname *fcnp, void *fde, struct vnode *fvp, struct vnode *tdvp, struct componentname *tcnp, void *tde, struct vnode *tvp) - Perform the physical file system rename operation, report any knotes, and
purge the namecache entries. Return zero on success or error on failure.
All file-system-independent error cases have been handled already.
File systems using fstrans(9) should use fstrans_start(9) and fstrans_done(9) here. fde and tde are the pointers that were supplied to
genfs_sane_rename
() and got passed to the gro_lookup operation to find information about directory entries.This may use
genfs_rename_knote
() to report any knotes, if the various file-system-dependent routines it uses to edit links don't do that already. This should usegenfs_rename_cache_purge
() to purge the namecache.fdvp and tdvp may be the same; every other pair of vnodes is guaranteed to be distinct. tvp may be null; every other vnode is guaranteed to be nonnull. All three or four vnodes are guaranteed to be referenced and locked.
- int
(*gro_remove_check_possible)
(struct mount *mp, struct vnode *dvp, struct vnode *vp) - Return zero if the file system might allow removing an entry in
dvp for vp independent of
credentials, or error if not. This should use
genfs_ufslike_remove_check_possible
() for file systems similar to UFS/FFS.dvp and vp are guaranteed to be distinct, nonnull, referenced, and locked.
This, and gro_remove_check_permitted below, are for renames that reduce to a remove; that is, renaming one entry to another when both entries refer to the same file. For reasons of locking insanity,
genfs_rename
cannot simply call VOP_REMOVE(9) instead. - int
(*gro_remove_check_permitted)
(struct mount *mp, kauth_cred_t cred, struct vnode *dvp, struct vnode *vp) - Return zero if the file system allows removing an entry in
dvp for vp given the
credentials cred, or error if not. This should use
genfs_ufslike_remove_check_permitted
() for file systems similar to UFS/FFS.dvp and vp are guaranteed to be distinct, nonnull, referenced, and locked.
- int
(*gro_remove)
(struct mount *mp, kauth_cred_t cred, struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp) - For a rename that is effectively a remove, perform the physical file
system remove operation, report any knotes, and purge the namecache
entries. Return zero on success or error on failure. All
file-system-independent error cases have been handled already.
File systems using fstrans(9) should use fstrans_start(9) and fstrans_done(9) here. de is one of the pointers that were supplied to
genfs_sane_rename
() and got passed to the gro_lookup operation to find information about directory entries.This should signal a
NOTE_WRITE
knote for dvp, and either aNOTE_DELETE
or aNOTE_LINK
knote for vp, depending on whether this removed the last link to it or not.dvp and vp are guaranteed to be distinct, nonnull, referenced, and locked.
The following utilities are provided for implementing the struct genfs_rename_ops operations:
genfs_rename_knote
(fdvp, fvp, tdvp, tvp)- Signal all the knotes relevant for the rename operation.
genfs_rename_cache_purge
(fdvp, fvp, tdvp, tvp)- Purge any namecache entries that the rename operation invalidates.
genfs_ufslike_rename_check_possible
(fdflags, fflags, tdflags, tflags, clobber, immutable, append)- Check whether the UFS/FFS-like flags of the files involved a rename allow
it. Return zero if allowed or error if not.
- fdflags
- flags of source directory
- fflags
- flags of source file
- tdflags
- flags of target directory
- tflags
- flags of target file, if there is one and clobber is true, or ignored otherwise
- clobber
- true if there is a target file whose entry will be clobbered or false if not
- immutable
- bit mask for the file system's immutable bit, like the UFS/FFS
IMMUTABLE
- append
- bit mask for the file system's append-only bit, like the UFS/FFS
APPEND
genfs_ufslike_rename_check_permitted
(cred, fdvp, fdmode, fduid, fvp, fuid, tdvp, tdmode, tduid, tvp, tuid)- Check whether the credentials cred are permitted by
the file ownership and permissions bits to perform a rename. Return zero
if permitted or error if not.
- cred
- caller's credentials
- fdvp
- source directory
- fdmode
- file permissions bits of fdvp
- fduid
- uid of the owner of fdvp
- fvp
- source file
- fuid
- uid of owner of fvp
- tdvp
- target directory
- tdmode
- file permissions bits of tdvp
- tduid
- uid of owner of tdvp
- tvp
- target file, if there is one, or
NULL
if not - tuid
- uid of owner of tvp, if there is a target file, or ignored otherwise
genfs_ufslike_remove_check_possible
(dflags, flags, immutable, append)- Check whether the UFS/FFS-like flags of the files involved a remove allow
it. Return zero if allowed or error if not.
- dflags
- flags of the directory
- flags
- flags of the file in the directory
- immutable
- bit mask for the file system's immutable bit, like the UFS/FFS
IMMUTABLE
- append
- bit mask for the file system's append-only bit, like the UFS/FFS
APPEND
genfs_ufslike_remove_check_permitted
(cred, dvp, dmode, duid, vp, uid)- Check whether the credentials cred are permitted by
the file ownership and permissions bits to perform a remove. Return zero
if permitted or error if not.
- cred
- caller's credentials
- dvp
- directory
- dmode
- file permissions bits of dvp
- duid
- uid of owner of dvp
- vp
- file in dvp
- uid
- uid of owner of vp
NOTES
Because there are so many cases of rename, it cannot be assumed a priori that any pairs of fdvp, fvp, tdvp, or fvp are distinct:
fdvp =
fvp |
rename("a/.",
"b") |
fdvp =
tdvp |
rename("a/b",
"a/c") |
fdvp =
tvp |
rename("a/b",
"a") |
fvp =
tdvp |
rename("a",
"a/b") |
fvp =
tvp |
rename("a",
"a") |
tdvp =
tvp |
rename("a",
"b/.") |
Handling all these cases correctly, and getting the locking
correct and deadlock-free, is very tricky, which is why
genfs_rename
exists. The interface to
genfs_rename
is very complicated because it must fit
the insane
VOP_RENAME(9) and
VOP_LOOKUP(9) protocols until we can fix them, and because it
must accommodate a variety of crufty file systems.
SEE ALSO
HISTORY
genfs_rename
was designed and implemented
by Taylor R. Campbell
<riastradh@NetBSD.org>
after many discussions with David Holland
<dholland@NetBSD.org>,
and first appeared in NetBSD 6.0.