I would appreciate any donations. Wishlist or send e-mail type donations to maekawa AT daemon-systems.org.

Thank you.


GENFS_RENAME(9)            Kernel Developer's Manual           GENFS_RENAME(9)

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.

NetBSD 7.1.2                      May 1, 2013                     NetBSD 7.1.2