GENFS_RENAME(9) | Kernel Developer's Manual | GENFS_RENAME(9) |
genfs_rename
,
genfs_insane_rename
,
genfs_sane_rename
—
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);
genfs_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:
NULL
if there was no entry beforeA 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
:
(*gro_genealogy)
(struct mount
*mp, kauth_cred_t cred, struct
vnode *fdvp, struct vnode *tdvp,
struct vnode **intermediate_node_ret)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, non-null, 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.
(*gro_lock_directory)
(struct mount
*mp, struct vnode *vp)(*gro_lookup)
(struct mount
*mp, struct vnode *dvp, struct
componentname *cnp, void *de,
struct vnode **vpp)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.
(*gro_directory_empty_p)
(struct
mount *mp, kauth_cred_t cred,
struct vnode *vp, struct vnode
*dvp)dvp and vp are guaranteed to be distinct, non-null, referenced, and locked.
(*gro_rename_check_possible)
(struct
mount *mp, struct vnode *fdvp,
struct vnode *fvp, struct vnode
*tdvp, struct vnode *tvp)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 non-null. All three or four vnodes are
guaranteed to be referenced and locked.
(*gro_rename_check_permitted)
(struct
mount *mp, kauth_cred_t cred,
struct vnode *fdvp, struct vnode
*fvp, struct vnode *tdvp, struct
vnode *tvp)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 non-null. All three or four vnodes are
guaranteed to be referenced and locked.
(*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)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 use
genfs_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 non-null. All three or four vnodes are guaranteed to be referenced and locked.
(*gro_remove_check_possible)
(struct
mount *mp, struct vnode *dvp,
struct vnode *vp)genfs_ufslike_remove_check_possible
() for file
systems similar to UFS/FFS.
dvp and vp are guaranteed to be distinct, non-null, 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.
(*gro_remove_check_permitted)
(struct
mount *mp, kauth_cred_t cred,
struct vnode *dvp, struct vnode
*vp)genfs_ufslike_remove_check_permitted
() for file
systems similar to UFS/FFS.
dvp and vp are guaranteed to be distinct, non-null, referenced, and locked.
(*gro_remove)
(struct mount
*mp, kauth_cred_t cred, struct
vnode *dvp, struct componentname *cnp,
void *de, struct vnode *vp)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 a
NOTE_DELETE
or a
NOTE_LINK
knote for vp,
depending on whether this removed the last link to it or not.
dvp and vp are guaranteed to be distinct, non-null, referenced, and locked.
The following utilities are provided for implementing the struct genfs_rename_ops operations:
genfs_rename_knote
(fdvp,
fvp, tdvp,
tvp)genfs_rename_cache_purge
(fdvp,
fvp, tdvp,
tvp)genfs_ufslike_rename_check_possible
(fdflags,
fflags, tdflags,
tflags, clobber,
immutable, append)IMMUTABLE
APPEND
genfs_ufslike_rename_check_permitted
(cred,
fdvp, fdmode,
fduid, fvp,
fuid, tdvp,
tdmode, tduid,
tvp, tuid)NULL
if
notgenfs_ufslike_remove_check_possible
(dflags,
flags, immutable,
append)IMMUTABLE
APPEND
genfs_ufslike_remove_check_permitted
(cred,
dvp, dmode,
duid, vp,
uid)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.
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.
May 1, 2013 | NetBSD 10.0 |