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
The
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:
-
-
- 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 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 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 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, 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 accomodate a variety of crufty file
systems.
SEE ALSO
genfs(9),
vfs(9),
vnodeops(9)
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.