Skip to content

Commit

Permalink
Support accessing .zfs/snapshot via NFS
Browse files Browse the repository at this point in the history
This patch is based on the previous work done by @andrey-ve and
@yshui.  It triggers the automount by using kern_path() to traverse
to the known snapshout mount point.  Once the snapshot is mounted
NFS can access the contents of the snapshot.

Signed-off-by: Brian Behlendorf <[email protected]>
Issue #2797
Issue #1655
Issue #616
  • Loading branch information
behlendorf committed Sep 3, 2015
1 parent 56d1aa6 commit 4144483
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 0 deletions.
111 changes: 111 additions & 0 deletions module/zfs/zfs_ctldir.c
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,45 @@ 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.
*/
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;

object = zsb->z_root;
objsetid = ZFSCTL_INO_SNAPDIRS - ip->i_ino;
zfid->zf_len = LONG_FID_LEN;

for (i = 0; i < sizeof (zfid->zf_object); i++)
zfid->zf_object[i] = (uint8_t)(object >> (8 * i));

for (i = 0; i < sizeof (zfid->zf_gen); i++)
zfid->zf_gen[i] = (uint8_t)(gen >> (8 * i));

for (i = 0; i < sizeof (zlfid->zf_setid); i++)
zlfid->zf_setid[i] = (uint8_t)(objsetid >> (8 * i));

for (i = 0; i < sizeof (zlfid->zf_setgen); i++)
zlfid->zf_setgen[i] = 0;

return (0);
}

/*
* Generate an appropriate fid for an entry in the .zfs directory.
*/
int
zfsctl_fid(struct inode *ip, fid_t *fidp)
{
Expand All @@ -603,6 +641,11 @@ zfsctl_fid(struct inode *ip, fid_t *fidp)
return (SET_ERROR(ENOSPC));
}

if (zfsctl_is_snapdir(ip)) {
ZFS_EXIT(zsb);
return (zfsctl_snapdir_fid(ip, fidp));
}

zfid = (zfid_short_t *)fidp;

zfid->zf_len = SHORT_FID_LEN;
Expand Down Expand Up @@ -671,6 +714,48 @@ zfsctl_snapshot_path(struct path *path, int len, char *full_path)
return (error);
}

/*
* Returns full path in full_path: "/pool/dataset/.zfs/snapshot/snap_name/"
*/
static int
zfsctl_snapshot_path_objset(zfs_sb_t *zsb, uint64_t objsetid,
int path_len, char *full_path)
{
objset_t *os = zsb->z_os;
fstrans_cookie_t cookie;
char *snapname;
boolean_t case_conflict;
uint64_t id, pos = 0;
int error = 0;

if (zsb->z_mntopts->z_mntpoint == NULL)
return (ENOENT);

cookie = spl_fstrans_mark();
snapname = kmem_alloc(MAXNAMELEN, KM_SLEEP);

while (error == 0) {
dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
error = dmu_snapshot_list_next(zsb->z_os, MAXNAMELEN,
snapname, &id, &pos, &case_conflict);
dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
if (error)
goto out;

if (id == objsetid)
break;
}

memset(full_path, 0, path_len);
snprintf(full_path, path_len - 1, "%s/.zfs/snapshot/%s",
zsb->z_mntopts->z_mntpoint, snapname);
out:
kmem_free(snapname, MAXNAMELEN);
spl_fstrans_unmark(cookie);

return (error);
}

/*
* Special case the handling of "..".
*/
Expand Down Expand Up @@ -1013,6 +1098,7 @@ zfsctl_snapshot_mount(struct path *path, int flags)
*/
zpl_follow_down_one(path);
snap_zsb = ITOZSB(path->dentry->d_inode);
snap_zsb->z_parent = zsb;
dentry = path->dentry;
path->mnt->mnt_flags |= MNT_SHRINKABLE;
zpl_follow_up(path);
Expand Down Expand Up @@ -1060,6 +1146,31 @@ zfsctl_lookup_objset(struct super_block *sb, uint64_t objsetid, zfs_sb_t **zsbp)
}
mutex_exit(&zfs_snapshot_lock);

/*
* Automount the snapshot given the objset id by constructing the
* full mount point and performing a traversal.
*/
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);
}

return (error);
}

Expand Down
2 changes: 2 additions & 0 deletions module/zfs/zfs_vfsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,8 @@ zfs_vget(struct super_block *sb, struct inode **ipp, fid_t *fidp)
zp_gen = zp_gen & gen_mask;
if (zp_gen == 0)
zp_gen = 1;
if ((fid_gen == 0) && (zsb->z_root == object))
fid_gen = zp_gen;
if (zp->z_unlinked || zp_gen != fid_gen) {
dprintf("znode gen (%llu) != fid gen (%llu)\n", zp_gen,
fid_gen);
Expand Down

0 comments on commit 4144483

Please sign in to comment.