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