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