diff options
Diffstat (limited to 'module/zfs/zfs_ctldir.c')
-rw-r--r-- | module/zfs/zfs_ctldir.c | 121 |
1 files changed, 58 insertions, 63 deletions
diff --git a/module/zfs/zfs_ctldir.c b/module/zfs/zfs_ctldir.c index e340462fb..8847db7f1 100644 --- a/module/zfs/zfs_ctldir.c +++ b/module/zfs/zfs_ctldir.c @@ -590,27 +590,40 @@ zfsctl_root(znode_t *zp) igrab(ZTOZSB(zp)->z_ctldir); return (ZTOZSB(zp)->z_ctldir); } + /* - * Generate a long fid which includes the root object and objset of a - * snapshot but not the generation number. For the root object the - * generation number is ignored when zero to avoid needing to open - * the dataset when generating fids for the snapshot names. + * Generate a long fid to indicate a snapdir. We encode whether snapdir is + * already monunted in gen field. We do this because nfsd lookup will not + * trigger automount. Next time the nfsd does fh_to_dentry, we will notice + * this and do automount and return ESTALE to force nfsd revalidate and follow + * mount. */ static int zfsctl_snapdir_fid(struct inode *ip, fid_t *fidp) { - zfs_sb_t *zsb = ITOZSB(ip); zfid_short_t *zfid = (zfid_short_t *)fidp; zfid_long_t *zlfid = (zfid_long_t *)fidp; uint32_t gen = 0; uint64_t object; uint64_t objsetid; int i; + struct dentry *dentry; + + if (fidp->fid_len < LONG_FID_LEN) { + fidp->fid_len = LONG_FID_LEN; + return (SET_ERROR(ENOSPC)); + } - object = zsb->z_root; + object = ip->i_ino; objsetid = ZFSCTL_INO_SNAPDIRS - ip->i_ino; zfid->zf_len = LONG_FID_LEN; + dentry = d_obtain_alias(igrab(ip)); + if (!IS_ERR(dentry)) { + gen = !!d_mountpoint(dentry); + dput(dentry); + } + for (i = 0; i < sizeof (zfid->zf_object); i++) zfid->zf_object[i] = (uint8_t)(object >> (8 * i)); @@ -640,15 +653,15 @@ zfsctl_fid(struct inode *ip, fid_t *fidp) ZFS_ENTER(zsb); - if (fidp->fid_len < SHORT_FID_LEN) { - fidp->fid_len = SHORT_FID_LEN; + if (zfsctl_is_snapdir(ip)) { ZFS_EXIT(zsb); - return (SET_ERROR(ENOSPC)); + return (zfsctl_snapdir_fid(ip, fidp)); } - if (zfsctl_is_snapdir(ip)) { + if (fidp->fid_len < SHORT_FID_LEN) { + fidp->fid_len = SHORT_FID_LEN; ZFS_EXIT(zsb); - return (zfsctl_snapdir_fid(ip, fidp)); + return (SET_ERROR(ENOSPC)); } zfid = (zfid_short_t *)fidp; @@ -1145,70 +1158,52 @@ error: } /* - * Given the objset id of the snapshot return its zfs_sb_t as zsbp. + * Get the snapdir inode from fid */ int -zfsctl_lookup_objset(struct super_block *sb, uint64_t objsetid, zfs_sb_t **zsbp) +zfsctl_snapdir_vget(struct super_block *sb, uint64_t objsetid, int gen, + struct inode **ipp) { - zfs_snapentry_t *se; int error; - spa_t *spa = ((zfs_sb_t *)(sb->s_fs_info))->z_os->os_spa; - - /* - * Verify that the snapshot is mounted then lookup the mounted root - * rather than the covered mount point. This may fail if the - * snapshot has just been unmounted by an unrelated user space - * process. This race cannot occur to an expired mount point - * because we hold the zfs_snapshot_lock to prevent the race. - */ - rw_enter(&zfs_snapshot_lock, RW_READER); - if ((se = zfsctl_snapshot_find_by_objsetid(spa, objsetid)) != NULL) { - zfs_sb_t *zsb; + struct path path; + char *mnt; + struct dentry *dentry; - zsb = ITOZSB(se->se_root_dentry->d_inode); - ASSERT3U(dmu_objset_id(zsb->z_os), ==, objsetid); + mnt = kmem_alloc(MAXPATHLEN, KM_SLEEP); - if (time_after(jiffies, zsb->z_snap_defer_time + - MAX(zfs_expire_snapshot * HZ / 2, HZ))) { - zsb->z_snap_defer_time = jiffies; - zfsctl_snapshot_unmount_cancel(se); - zfsctl_snapshot_unmount_delay_impl(se, - zfs_expire_snapshot); - } + error = zfsctl_snapshot_path_objset(sb->s_fs_info, objsetid, + MAXPATHLEN, mnt); + if (error) + goto out; - *zsbp = zsb; - zfsctl_snapshot_rele(se); - error = SET_ERROR(0); - } else { - error = SET_ERROR(ENOENT); - } - rw_exit(&zfs_snapshot_lock); + /* Trigger automount */ + error = kern_path(mnt, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &path); + if (error) + goto out; + path_put(&path); /* - * Automount the snapshot given the objset id by constructing the - * full mount point and performing a traversal. + * Get the snapdir inode. Note, we don't want to use the above + * path because it contains the root of the snapshot rather + * than the snapdir. */ - if (error == ENOENT) { - struct path path; - char *mnt; - - mnt = kmem_alloc(MAXPATHLEN, KM_SLEEP); - error = zfsctl_snapshot_path_objset(sb->s_fs_info, objsetid, - MAXPATHLEN, mnt); - if (error) { - kmem_free(mnt, MAXPATHLEN); - return (SET_ERROR(error)); - } - - error = kern_path(mnt, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &path); - if (error == 0) { - *zsbp = ITOZSB(path.dentry->d_inode); - path_put(&path); - } - - kmem_free(mnt, MAXPATHLEN); + *ipp = ilookup(sb, ZFSCTL_INO_SNAPDIRS - objsetid); + if (*ipp == NULL) { + error = SET_ERROR(ENOENT); + goto out; } + /* check gen, see zfsctl_snapdir_fid */ + dentry = d_obtain_alias(igrab(*ipp)); + if (gen != (!IS_ERR(dentry) && d_mountpoint(dentry))) { + iput(*ipp); + *ipp = NULL; + error = SET_ERROR(ENOENT); + } + if (!IS_ERR(dentry)) + dput(dentry); +out: + kmem_free(mnt, MAXPATHLEN); return (error); } |