diff --git a/src/smoothfs/dir.c b/src/smoothfs/dir.c index b2d71c1..70c3c4b 100644 --- a/src/smoothfs/dir.c +++ b/src/smoothfs/dir.c @@ -130,34 +130,6 @@ static char *smoothfs_rel_path_from_dentry(struct dentry *dentry) return rel; } -static int smoothfs_resolve_rel_path_on_tier(struct smoothfs_sb_info *sbi, - u8 tier, const char *rel_path, - struct path *out) -{ - char *buf, *rendered, *full = NULL; - int err; - - buf = kmalloc(PATH_MAX, GFP_KERNEL); - if (!buf) - return -ENOMEM; - rendered = d_path(&sbi->tiers[tier].lower_path, buf, PATH_MAX); - if (IS_ERR(rendered)) { - err = PTR_ERR(rendered); - kfree(buf); - return err; - } - if (rel_path && *rel_path) - full = kasprintf(GFP_KERNEL, "%s/%s", rendered, rel_path); - else - full = kstrdup(rendered, GFP_KERNEL); - kfree(buf); - if (!full) - return -ENOMEM; - err = kern_path(full, LOOKUP_FOLLOW, out); - kfree(full); - return err; -} - static int smoothfs_collect_one_dir(struct smoothfs_dir_cache *cache, struct path *dir_path, const struct cred *cred) diff --git a/src/smoothfs/inode.c b/src/smoothfs/inode.c index 02440a9..542cb52 100644 --- a/src/smoothfs/inode.c +++ b/src/smoothfs/inode.c @@ -49,34 +49,6 @@ static char *smoothfs_rel_path_from_dentry(struct dentry *dentry) return rel; } -static int smoothfs_resolve_rel_path_on_tier(struct smoothfs_sb_info *sbi, - u8 tier, const char *rel_path, - struct path *out) -{ - char *buf, *rendered, *full = NULL; - int err; - - buf = kmalloc(PATH_MAX, GFP_KERNEL); - if (!buf) - return -ENOMEM; - rendered = d_path(&sbi->tiers[tier].lower_path, buf, PATH_MAX); - if (IS_ERR(rendered)) { - err = PTR_ERR(rendered); - kfree(buf); - return err; - } - if (rel_path && *rel_path) - full = kasprintf(GFP_KERNEL, "%s/%s", rendered, rel_path); - else - full = kstrdup(rendered, GFP_KERNEL); - kfree(buf); - if (!full) - return -ENOMEM; - err = kern_path(full, LOOKUP_FOLLOW, out); - kfree(full); - return err; -} - static bool smoothfs_tier_near_enospc(struct smoothfs_sb_info *sbi, u8 tier) { struct kstatfs st; diff --git a/src/smoothfs/smoothfs.h b/src/smoothfs/smoothfs.h index db240f7..dcce3d7 100644 --- a/src/smoothfs/smoothfs.h +++ b/src/smoothfs/smoothfs.h @@ -313,6 +313,32 @@ struct smoothfs_sb_info { void *sysfs_pool; }; +/* + * Resolve a pool-relative path from the lower mount that smoothfs pinned at + * mount time. Never render that path and feed it back to kern_path(): + * kern_path() starts in the calling task's mount namespace, where containers + * need not expose the backing-tier mountpoints at all. + */ +static inline int +smoothfs_resolve_rel_path_on_tier(struct smoothfs_sb_info *sbi, u8 tier, + const char *rel_path, struct path *out) +{ + struct path root; + + if (tier >= sbi->ntiers) + return -EINVAL; + + root = sbi->tiers[tier].lower_path; + if (!rel_path || !*rel_path) { + *out = root; + path_get(out); + return 0; + } + + return vfs_path_lookup(root.dentry, root.mnt, rel_path, + LOOKUP_FOLLOW, out); +} + static inline u8 smoothfs_tier_of(struct smoothfs_sb_info *sbi, struct vfsmount *mnt) { diff --git a/src/smoothfs/super.c b/src/smoothfs/super.c index e8bc01c..724ef1c 100644 --- a/src/smoothfs/super.c +++ b/src/smoothfs/super.c @@ -697,37 +697,6 @@ static void smoothfs_write_staging_set_reason(struct smoothfs_sb_info *sbi, spin_unlock(&sbi->write_staging_lock); } -static int smoothfs_resolve_rel_path_on_tier(struct smoothfs_sb_info *sbi, - u8 tier, const char *rel_path, - struct path *out) -{ - char *buf, *rendered, *full = NULL; - int err; - - if (tier >= sbi->ntiers) - return -EINVAL; - - buf = kmalloc(PATH_MAX, GFP_KERNEL); - if (!buf) - return -ENOMEM; - rendered = d_path(&sbi->tiers[tier].lower_path, buf, PATH_MAX); - if (IS_ERR(rendered)) { - err = PTR_ERR(rendered); - kfree(buf); - return err; - } - if (rel_path && *rel_path) - full = kasprintf(GFP_KERNEL, "%s/%s", rendered, rel_path); - else - full = kstrdup(rendered, GFP_KERNEL); - kfree(buf); - if (!full) - return -ENOMEM; - err = kern_path(full, LOOKUP_FOLLOW, out); - kfree(full); - return err; -} - /* * Drop the replay pin on the separately-tracked smoothfs inode that shadows a * spill/replica copy we just removed from `tier`. diff --git a/src/smoothfs/test/run_runtime_harnesses.sh b/src/smoothfs/test/run_runtime_harnesses.sh index 82edf86..0ddb363 100755 --- a/src/smoothfs/test/run_runtime_harnesses.sh +++ b/src/smoothfs/test/run_runtime_harnesses.sh @@ -28,6 +28,7 @@ core_tests=( tier_spill_nested_parent.sh tier_spill_union_readdir.sh tier_spill_negative_dentry_revalidate.sh + tier_spill_mount_namespace_lookup.sh tier_spill_unlink_finds_right_tier.sh tier_spill_rmdir_placement.sh tier_spill_forget_lower_reclaim.sh diff --git a/src/smoothfs/test/tier_spill_mount_namespace_lookup.sh b/src/smoothfs/test/tier_spill_mount_namespace_lookup.sh new file mode 100755 index 0000000..b5fe89e --- /dev/null +++ b/src/smoothfs/test/tier_spill_mount_namespace_lookup.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Regression: lower-tier resolution must not depend on the calling task's +# mount namespace. Containers see the merged bind mount but intentionally do +# not see SmoothNAS's backing-tier mountpoints. + +set -u + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +export SPILL_ROOT=${SPILL_ROOT:-/tmp/smoothfs-mount-namespace-lookup} +. "$SCRIPT_DIR/tier_spill_lib.sh" + +trap spill_cleanup EXIT + +echo "=== laying down 2-tier XFS smoothfs ===" +spill_setup_pool nslookup 00000000-0000-0000-0000-00000000f10f + +mkdir -p "$SPILL_ROOT/slow/library" +printf 'namespace-independent\n' > "$SPILL_ROOT/slow/library/episode.mkv" +sync + +echo "=== hide lower mountpoints from a container-like mount namespace ===" +VIEW="$SPILL_ROOT/container-view" +mkdir -p "$VIEW" +if unshare --mount bash -eu -c ' + mount --bind "$1/server" "$2" + umount -l "$1/server" + umount -l "$1/fast" + umount -l "$1/slow" + + # The caller can no longer resolve the host backing path by name. + test ! -e "$1/slow/library/episode.mkv" + # Smoothfs must still resolve through its pinned lower struct path. + test -f "$2/library/episode.mkv" + test "$(cat "$2/library/episode.mkv")" = namespace-independent + ls "$2/library" | grep -qx episode.mkv +' _ "$SPILL_ROOT" "$VIEW"; then + spill_assert true +else + spill_assert false "merged lookup failed outside the host mount namespace" +fi + +spill_finish "tier_spill_mount_namespace_lookup"