301 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/fs.h>4#include <linux/types.h>5#include "ctree.h"6#include "disk-io.h"7#include "btrfs_inode.h"8#include "export.h"9#include "accessors.h"10#include "super.h"11 12#define BTRFS_FID_SIZE_NON_CONNECTABLE (offsetof(struct btrfs_fid, \13 parent_objectid) / 4)14#define BTRFS_FID_SIZE_CONNECTABLE (offsetof(struct btrfs_fid, \15 parent_root_objectid) / 4)16#define BTRFS_FID_SIZE_CONNECTABLE_ROOT (sizeof(struct btrfs_fid) / 4)17 18static int btrfs_encode_fh(struct inode *inode, u32 *fh, int *max_len,19 struct inode *parent)20{21 struct btrfs_fid *fid = (struct btrfs_fid *)fh;22 int len = *max_len;23 int type;24 25 if (parent && (len < BTRFS_FID_SIZE_CONNECTABLE)) {26 *max_len = BTRFS_FID_SIZE_CONNECTABLE;27 return FILEID_INVALID;28 } else if (len < BTRFS_FID_SIZE_NON_CONNECTABLE) {29 *max_len = BTRFS_FID_SIZE_NON_CONNECTABLE;30 return FILEID_INVALID;31 }32 33 len = BTRFS_FID_SIZE_NON_CONNECTABLE;34 type = FILEID_BTRFS_WITHOUT_PARENT;35 36 fid->objectid = btrfs_ino(BTRFS_I(inode));37 fid->root_objectid = btrfs_root_id(BTRFS_I(inode)->root);38 fid->gen = inode->i_generation;39 40 if (parent) {41 u64 parent_root_id;42 43 fid->parent_objectid = btrfs_ino(BTRFS_I(parent));44 fid->parent_gen = parent->i_generation;45 parent_root_id = btrfs_root_id(BTRFS_I(parent)->root);46 47 if (parent_root_id != fid->root_objectid) {48 fid->parent_root_objectid = parent_root_id;49 len = BTRFS_FID_SIZE_CONNECTABLE_ROOT;50 type = FILEID_BTRFS_WITH_PARENT_ROOT;51 } else {52 len = BTRFS_FID_SIZE_CONNECTABLE;53 type = FILEID_BTRFS_WITH_PARENT;54 }55 }56 57 *max_len = len;58 return type;59}60 61/*62 * Read dentry of inode with @objectid from filesystem root @root_objectid.63 *64 * @sb: the filesystem super block65 * @objectid: inode objectid66 * @root_objectid: object id of the subvolume root where to look up the inode67 * @generation: optional, if not zero, verify that the found inode68 * generation matches69 *70 * Return dentry alias for the inode, otherwise an error. In case the71 * generation does not match return ESTALE.72 */73struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,74 u64 root_objectid, u64 generation)75{76 struct btrfs_fs_info *fs_info = btrfs_sb(sb);77 struct btrfs_root *root;78 struct inode *inode;79 80 if (objectid < BTRFS_FIRST_FREE_OBJECTID)81 return ERR_PTR(-ESTALE);82 83 root = btrfs_get_fs_root(fs_info, root_objectid, true);84 if (IS_ERR(root))85 return ERR_CAST(root);86 87 inode = btrfs_iget(objectid, root);88 btrfs_put_root(root);89 if (IS_ERR(inode))90 return ERR_CAST(inode);91 92 if (generation != 0 && generation != inode->i_generation) {93 iput(inode);94 return ERR_PTR(-ESTALE);95 }96 97 return d_obtain_alias(inode);98}99 100static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh,101 int fh_len, int fh_type)102{103 struct btrfs_fid *fid = (struct btrfs_fid *) fh;104 u64 objectid, root_objectid;105 u32 generation;106 107 if (fh_type == FILEID_BTRFS_WITH_PARENT) {108 if (fh_len < BTRFS_FID_SIZE_CONNECTABLE)109 return NULL;110 root_objectid = fid->root_objectid;111 } else if (fh_type == FILEID_BTRFS_WITH_PARENT_ROOT) {112 if (fh_len < BTRFS_FID_SIZE_CONNECTABLE_ROOT)113 return NULL;114 root_objectid = fid->parent_root_objectid;115 } else116 return NULL;117 118 objectid = fid->parent_objectid;119 generation = fid->parent_gen;120 121 return btrfs_get_dentry(sb, objectid, root_objectid, generation);122}123 124static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,125 int fh_len, int fh_type)126{127 struct btrfs_fid *fid = (struct btrfs_fid *) fh;128 u64 objectid, root_objectid;129 u32 generation;130 131 if ((fh_type != FILEID_BTRFS_WITH_PARENT ||132 fh_len < BTRFS_FID_SIZE_CONNECTABLE) &&133 (fh_type != FILEID_BTRFS_WITH_PARENT_ROOT ||134 fh_len < BTRFS_FID_SIZE_CONNECTABLE_ROOT) &&135 (fh_type != FILEID_BTRFS_WITHOUT_PARENT ||136 fh_len < BTRFS_FID_SIZE_NON_CONNECTABLE))137 return NULL;138 139 objectid = fid->objectid;140 root_objectid = fid->root_objectid;141 generation = fid->gen;142 143 return btrfs_get_dentry(sb, objectid, root_objectid, generation);144}145 146struct dentry *btrfs_get_parent(struct dentry *child)147{148 struct inode *dir = d_inode(child);149 struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);150 struct btrfs_root *root = BTRFS_I(dir)->root;151 struct btrfs_path *path;152 struct extent_buffer *leaf;153 struct btrfs_root_ref *ref;154 struct btrfs_key key;155 struct btrfs_key found_key;156 int ret;157 158 path = btrfs_alloc_path();159 if (!path)160 return ERR_PTR(-ENOMEM);161 162 if (btrfs_ino(BTRFS_I(dir)) == BTRFS_FIRST_FREE_OBJECTID) {163 key.objectid = btrfs_root_id(root);164 key.type = BTRFS_ROOT_BACKREF_KEY;165 key.offset = (u64)-1;166 root = fs_info->tree_root;167 } else {168 key.objectid = btrfs_ino(BTRFS_I(dir));169 key.type = BTRFS_INODE_REF_KEY;170 key.offset = (u64)-1;171 }172 173 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);174 if (ret < 0)175 goto fail;176 if (ret == 0) {177 /*178 * Key with offset of -1 found, there would have to exist an179 * inode with such number or a root with such id.180 */181 ret = -EUCLEAN;182 goto fail;183 }184 185 if (path->slots[0] == 0) {186 ret = -ENOENT;187 goto fail;188 }189 190 path->slots[0]--;191 leaf = path->nodes[0];192 193 btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);194 if (found_key.objectid != key.objectid || found_key.type != key.type) {195 ret = -ENOENT;196 goto fail;197 }198 199 if (found_key.type == BTRFS_ROOT_BACKREF_KEY) {200 ref = btrfs_item_ptr(leaf, path->slots[0],201 struct btrfs_root_ref);202 key.objectid = btrfs_root_ref_dirid(leaf, ref);203 } else {204 key.objectid = found_key.offset;205 }206 btrfs_free_path(path);207 208 if (found_key.type == BTRFS_ROOT_BACKREF_KEY) {209 return btrfs_get_dentry(fs_info->sb, key.objectid,210 found_key.offset, 0);211 }212 213 return d_obtain_alias(btrfs_iget(key.objectid, root));214fail:215 btrfs_free_path(path);216 return ERR_PTR(ret);217}218 219static int btrfs_get_name(struct dentry *parent, char *name,220 struct dentry *child)221{222 struct inode *inode = d_inode(child);223 struct inode *dir = d_inode(parent);224 struct btrfs_fs_info *fs_info = inode_to_fs_info(inode);225 struct btrfs_path *path;226 struct btrfs_root *root = BTRFS_I(dir)->root;227 struct btrfs_inode_ref *iref;228 struct btrfs_root_ref *rref;229 struct extent_buffer *leaf;230 unsigned long name_ptr;231 struct btrfs_key key;232 int name_len;233 int ret;234 u64 ino;235 236 if (!S_ISDIR(dir->i_mode))237 return -EINVAL;238 239 ino = btrfs_ino(BTRFS_I(inode));240 241 path = btrfs_alloc_path();242 if (!path)243 return -ENOMEM;244 245 if (ino == BTRFS_FIRST_FREE_OBJECTID) {246 key.objectid = btrfs_root_id(BTRFS_I(inode)->root);247 key.type = BTRFS_ROOT_BACKREF_KEY;248 key.offset = (u64)-1;249 root = fs_info->tree_root;250 } else {251 key.objectid = ino;252 key.offset = btrfs_ino(BTRFS_I(dir));253 key.type = BTRFS_INODE_REF_KEY;254 }255 256 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);257 if (ret < 0) {258 btrfs_free_path(path);259 return ret;260 } else if (ret > 0) {261 if (ino == BTRFS_FIRST_FREE_OBJECTID) {262 path->slots[0]--;263 } else {264 btrfs_free_path(path);265 return -ENOENT;266 }267 }268 leaf = path->nodes[0];269 270 if (ino == BTRFS_FIRST_FREE_OBJECTID) {271 rref = btrfs_item_ptr(leaf, path->slots[0],272 struct btrfs_root_ref);273 name_ptr = (unsigned long)(rref + 1);274 name_len = btrfs_root_ref_name_len(leaf, rref);275 } else {276 iref = btrfs_item_ptr(leaf, path->slots[0],277 struct btrfs_inode_ref);278 name_ptr = (unsigned long)(iref + 1);279 name_len = btrfs_inode_ref_name_len(leaf, iref);280 }281 282 read_extent_buffer(leaf, name, name_ptr, name_len);283 btrfs_free_path(path);284 285 /*286 * have to add the null termination to make sure that reconnect_path287 * gets the right len for strlen288 */289 name[name_len] = '\0';290 291 return 0;292}293 294const struct export_operations btrfs_export_ops = {295 .encode_fh = btrfs_encode_fh,296 .fh_to_dentry = btrfs_fh_to_dentry,297 .fh_to_parent = btrfs_fh_to_parent,298 .get_parent = btrfs_get_parent,299 .get_name = btrfs_get_name,300};301