This helper walks an input path to its parent. Logic are added to handle walking across mount tree. This will be used by landlock, and BPF LSM. Signed-off-by: Song Liu <song@xxxxxxxxxx> --- fs/namei.c | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/namei.h | 8 ++++++++ 2 files changed, 47 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 84a0e0b0111c..475f4188a0e6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1379,6 +1379,45 @@ int follow_up(struct path *path) } EXPORT_SYMBOL(follow_up); +/** + * path_parent - Find the parent of path + * @path: input and output path. + * + * Given a path, find the parent path. Replace @path with the parent path. + * If we were already at the real root or a disconnected root, @path is + * not changed. + * + * Returns: + * PATH_PARENT_SAME_MOUNT - if we walked up within the same mount; + * PATH_PARENT_CHANGED_MOUNT - if we walked up a mount point; + * PATH_PARENT_REAL_ROOT - if we were already at real root; + * PATH_PARENT_DISCONNECTED_ROOT - if we were at the root of a disconnected + * filesystem. + */ +enum path_parent_status path_parent(struct path *path) +{ + struct dentry *parent; + + if (path->dentry == path->mnt->mnt_root) { + if (!follow_up(path)) + return PATH_PARENT_REAL_ROOT; + return PATH_PARENT_CHANGED_MOUNT; + } + if (unlikely(IS_ROOT(path->dentry))) + return PATH_PARENT_DISCONNECTED_ROOT; + + parent = dget_parent(path->dentry); + if (unlikely(!path_connected(path->mnt, parent))) { + dput(parent); + return PATH_PARENT_DISCONNECTED_ROOT; + } + + dput(path->dentry); + path->dentry = parent; + return PATH_PARENT_SAME_MOUNT; +} +EXPORT_SYMBOL_GPL(path_parent); + static bool choose_mountpoint_rcu(struct mount *m, const struct path *root, struct path *path, unsigned *seqp) { diff --git a/include/linux/namei.h b/include/linux/namei.h index bbaf55fb3101..dc6e9096eb15 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -86,6 +86,14 @@ extern int follow_down_one(struct path *); extern int follow_down(struct path *path, unsigned int flags); extern int follow_up(struct path *); +enum path_parent_status { + PATH_PARENT_SAME_MOUNT = 0, + PATH_PARENT_CHANGED_MOUNT, + PATH_PARENT_REAL_ROOT, + PATH_PARENT_DISCONNECTED_ROOT, +}; +enum path_parent_status path_parent(struct path *path); + extern struct dentry *lock_rename(struct dentry *, struct dentry *); extern struct dentry *lock_rename_child(struct dentry *, struct dentry *); extern void unlock_rename(struct dentry *, struct dentry *); -- 2.47.1