aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/openzfs/module/os/linux/zfs/zfs_ctldir.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/openzfs/module/os/linux/zfs/zfs_ctldir.c')
-rw-r--r--sys/contrib/openzfs/module/os/linux/zfs/zfs_ctldir.c109
1 files changed, 104 insertions, 5 deletions
diff --git a/sys/contrib/openzfs/module/os/linux/zfs/zfs_ctldir.c b/sys/contrib/openzfs/module/os/linux/zfs/zfs_ctldir.c
index 8a42a075cd25..f60d6ae91e0b 100644
--- a/sys/contrib/openzfs/module/os/linux/zfs/zfs_ctldir.c
+++ b/sys/contrib/openzfs/module/os/linux/zfs/zfs_ctldir.c
@@ -767,9 +767,6 @@ zfsctl_snapshot_path_objset(zfsvfs_t *zfsvfs, uint64_t objsetid,
uint64_t id, pos = 0;
int error = 0;
- if (zfsvfs->z_vfs->vfs_mntpoint == NULL)
- return (SET_ERROR(ENOENT));
-
cookie = spl_fstrans_mark();
snapname = kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP);
@@ -786,8 +783,14 @@ zfsctl_snapshot_path_objset(zfsvfs_t *zfsvfs, uint64_t objsetid,
break;
}
- snprintf(full_path, path_len, "%s/.zfs/snapshot/%s",
- zfsvfs->z_vfs->vfs_mntpoint, snapname);
+ mutex_enter(&zfsvfs->z_vfs->vfs_mntpt_lock);
+ if (zfsvfs->z_vfs->vfs_mntpoint != NULL) {
+ snprintf(full_path, path_len, "%s/.zfs/snapshot/%s",
+ zfsvfs->z_vfs->vfs_mntpoint, snapname);
+ } else
+ error = SET_ERROR(ENOENT);
+ mutex_exit(&zfsvfs->z_vfs->vfs_mntpt_lock);
+
out:
kmem_free(snapname, ZFS_MAX_DATASET_NAME_LEN);
spl_fstrans_unmark(cookie);
@@ -1050,6 +1053,66 @@ exportfs_flush(void)
}
/*
+ * Returns the path in char format for given struct path. Uses
+ * d_path exported by kernel to convert struct path to char
+ * format. Returns the correct path for mountpoints and chroot
+ * environments.
+ *
+ * If chroot environment has directories that are mounted with
+ * --bind or --rbind flag, d_path returns the complete path inside
+ * chroot environment but does not return the absolute path, i.e.
+ * the path to chroot environment is missing.
+ */
+static int
+get_root_path(struct path *path, char *buff, int len)
+{
+ char *path_buffer, *path_ptr;
+ int error = 0;
+
+ path_get(path);
+ path_buffer = kmem_zalloc(len, KM_SLEEP);
+ path_ptr = d_path(path, path_buffer, len);
+ if (IS_ERR(path_ptr))
+ error = SET_ERROR(-PTR_ERR(path_ptr));
+ else
+ strcpy(buff, path_ptr);
+
+ kmem_free(path_buffer, len);
+ path_put(path);
+ return (error);
+}
+
+/*
+ * Returns if the current process root is chrooted or not. Linux
+ * kernel exposes the task_struct for current process and init.
+ * Since init process root points to actual root filesystem when
+ * Linux runtime is reached, we can compare the current process
+ * root with init process root to determine if root of the current
+ * process is different from init, which can reliably determine if
+ * current process is in chroot context or not.
+ */
+static int
+is_current_chrooted(void)
+{
+ struct task_struct *curr = current, *global = &init_task;
+ struct path cr_root, gl_root;
+
+ task_lock(curr);
+ get_fs_root(curr->fs, &cr_root);
+ task_unlock(curr);
+
+ task_lock(global);
+ get_fs_root(global->fs, &gl_root);
+ task_unlock(global);
+
+ int chrooted = !path_equal(&cr_root, &gl_root);
+ path_put(&gl_root);
+ path_put(&cr_root);
+
+ return (chrooted);
+}
+
+/*
* Attempt to unmount a snapshot by making a call to user space.
* There is no assurance that this can or will succeed, is just a
* best effort. In the case where it does fail, perhaps because
@@ -1123,14 +1186,50 @@ zfsctl_snapshot_mount(struct path *path, int flags)
if (error)
goto error;
+ if (is_current_chrooted() == 0) {
+ /*
+ * Current process is not in chroot context
+ */
+
+ char *m = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ struct path mnt_path;
+ mnt_path.mnt = path->mnt;
+ mnt_path.dentry = path->mnt->mnt_root;
+
+ /*
+ * Get path to current mountpoint
+ */
+ error = get_root_path(&mnt_path, m, MAXPATHLEN);
+ if (error != 0) {
+ kmem_free(m, MAXPATHLEN);
+ goto error;
+ }
+ mutex_enter(&zfsvfs->z_vfs->vfs_mntpt_lock);
+ if (zfsvfs->z_vfs->vfs_mntpoint != NULL) {
+ /*
+ * If current mnountpoint and vfs_mntpoint are not same,
+ * store current mountpoint in vfs_mntpoint.
+ */
+ if (strcmp(zfsvfs->z_vfs->vfs_mntpoint, m) != 0) {
+ kmem_strfree(zfsvfs->z_vfs->vfs_mntpoint);
+ zfsvfs->z_vfs->vfs_mntpoint = kmem_strdup(m);
+ }
+ } else
+ zfsvfs->z_vfs->vfs_mntpoint = kmem_strdup(m);
+ mutex_exit(&zfsvfs->z_vfs->vfs_mntpt_lock);
+ kmem_free(m, MAXPATHLEN);
+ }
+
/*
* Construct a mount point path from sb of the ctldir inode and dirent
* name, instead of from d_path(), so that chroot'd process doesn't fail
* on mount.zfs(8).
*/
+ mutex_enter(&zfsvfs->z_vfs->vfs_mntpt_lock);
snprintf(full_path, MAXPATHLEN, "%s/.zfs/snapshot/%s",
zfsvfs->z_vfs->vfs_mntpoint ? zfsvfs->z_vfs->vfs_mntpoint : "",
dname(dentry));
+ mutex_exit(&zfsvfs->z_vfs->vfs_mntpt_lock);
snprintf(options, 7, "%s",
zfs_snapshot_no_setuid ? "nosuid" : "suid");