linux/fs/btrfs/inode-item.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 Oracle.  All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public
   6 * License v2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11 * General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public
  14 * License along with this program; if not, write to the
  15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  16 * Boston, MA 021110-1307, USA.
  17 */
  18
  19#include "ctree.h"
  20#include "disk-io.h"
  21#include "hash.h"
  22#include "transaction.h"
  23#include "print-tree.h"
  24
  25static int find_name_in_backref(struct btrfs_path *path, const char *name,
  26                         int name_len, struct btrfs_inode_ref **ref_ret)
  27{
  28        struct extent_buffer *leaf;
  29        struct btrfs_inode_ref *ref;
  30        unsigned long ptr;
  31        unsigned long name_ptr;
  32        u32 item_size;
  33        u32 cur_offset = 0;
  34        int len;
  35
  36        leaf = path->nodes[0];
  37        item_size = btrfs_item_size_nr(leaf, path->slots[0]);
  38        ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
  39        while (cur_offset < item_size) {
  40                ref = (struct btrfs_inode_ref *)(ptr + cur_offset);
  41                len = btrfs_inode_ref_name_len(leaf, ref);
  42                name_ptr = (unsigned long)(ref + 1);
  43                cur_offset += len + sizeof(*ref);
  44                if (len != name_len)
  45                        continue;
  46                if (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) {
  47                        *ref_ret = ref;
  48                        return 1;
  49                }
  50        }
  51        return 0;
  52}
  53
  54int btrfs_find_name_in_ext_backref(struct btrfs_path *path, u64 ref_objectid,
  55                                   const char *name, int name_len,
  56                                   struct btrfs_inode_extref **extref_ret)
  57{
  58        struct extent_buffer *leaf;
  59        struct btrfs_inode_extref *extref;
  60        unsigned long ptr;
  61        unsigned long name_ptr;
  62        u32 item_size;
  63        u32 cur_offset = 0;
  64        int ref_name_len;
  65
  66        leaf = path->nodes[0];
  67        item_size = btrfs_item_size_nr(leaf, path->slots[0]);
  68        ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
  69
  70        /*
  71         * Search all extended backrefs in this item. We're only
  72         * looking through any collisions so most of the time this is
  73         * just going to compare against one buffer. If all is well,
  74         * we'll return success and the inode ref object.
  75         */
  76        while (cur_offset < item_size) {
  77                extref = (struct btrfs_inode_extref *) (ptr + cur_offset);
  78                name_ptr = (unsigned long)(&extref->name);
  79                ref_name_len = btrfs_inode_extref_name_len(leaf, extref);
  80
  81                if (ref_name_len == name_len &&
  82                    btrfs_inode_extref_parent(leaf, extref) == ref_objectid &&
  83                    (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)) {
  84                        if (extref_ret)
  85                                *extref_ret = extref;
  86                        return 1;
  87                }
  88
  89                cur_offset += ref_name_len + sizeof(*extref);
  90        }
  91        return 0;
  92}
  93
  94/* Returns NULL if no extref found */
  95struct btrfs_inode_extref *
  96btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
  97                          struct btrfs_root *root,
  98                          struct btrfs_path *path,
  99                          const char *name, int name_len,
 100                          u64 inode_objectid, u64 ref_objectid, int ins_len,
 101                          int cow)
 102{
 103        int ret;
 104        struct btrfs_key key;
 105        struct btrfs_inode_extref *extref;
 106
 107        key.objectid = inode_objectid;
 108        key.type = BTRFS_INODE_EXTREF_KEY;
 109        key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
 110
 111        ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
 112        if (ret < 0)
 113                return ERR_PTR(ret);
 114        if (ret > 0)
 115                return NULL;
 116        if (!btrfs_find_name_in_ext_backref(path, ref_objectid, name, name_len, &extref))
 117                return NULL;
 118        return extref;
 119}
 120
 121static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
 122                                  struct btrfs_root *root,
 123                                  const char *name, int name_len,
 124                                  u64 inode_objectid, u64 ref_objectid,
 125                                  u64 *index)
 126{
 127        struct btrfs_path *path;
 128        struct btrfs_key key;
 129        struct btrfs_inode_extref *extref;
 130        struct extent_buffer *leaf;
 131        int ret;
 132        int del_len = name_len + sizeof(*extref);
 133        unsigned long ptr;
 134        unsigned long item_start;
 135        u32 item_size;
 136
 137        key.objectid = inode_objectid;
 138        key.type = BTRFS_INODE_EXTREF_KEY;
 139        key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
 140
 141        path = btrfs_alloc_path();
 142        if (!path)
 143                return -ENOMEM;
 144
 145        path->leave_spinning = 1;
 146
 147        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
 148        if (ret > 0)
 149                ret = -ENOENT;
 150        if (ret < 0)
 151                goto out;
 152
 153        /*
 154         * Sanity check - did we find the right item for this name?
 155         * This should always succeed so error here will make the FS
 156         * readonly.
 157         */
 158        if (!btrfs_find_name_in_ext_backref(path, ref_objectid,
 159                                            name, name_len, &extref)) {
 160                btrfs_handle_fs_error(root->fs_info, -ENOENT, NULL);
 161                ret = -EROFS;
 162                goto out;
 163        }
 164
 165        leaf = path->nodes[0];
 166        item_size = btrfs_item_size_nr(leaf, path->slots[0]);
 167        if (index)
 168                *index = btrfs_inode_extref_index(leaf, extref);
 169
 170        if (del_len == item_size) {
 171                /*
 172                 * Common case only one ref in the item, remove the
 173                 * whole item.
 174                 */
 175                ret = btrfs_del_item(trans, root, path);
 176                goto out;
 177        }
 178
 179        ptr = (unsigned long)extref;
 180        item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
 181
 182        memmove_extent_buffer(leaf, ptr, ptr + del_len,
 183                              item_size - (ptr + del_len - item_start));
 184
 185        btrfs_truncate_item(root->fs_info, path, item_size - del_len, 1);
 186
 187out:
 188        btrfs_free_path(path);
 189
 190        return ret;
 191}
 192
 193int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
 194                        struct btrfs_root *root,
 195                        const char *name, int name_len,
 196                        u64 inode_objectid, u64 ref_objectid, u64 *index)
 197{
 198        struct btrfs_path *path;
 199        struct btrfs_key key;
 200        struct btrfs_inode_ref *ref;
 201        struct extent_buffer *leaf;
 202        unsigned long ptr;
 203        unsigned long item_start;
 204        u32 item_size;
 205        u32 sub_item_len;
 206        int ret;
 207        int search_ext_refs = 0;
 208        int del_len = name_len + sizeof(*ref);
 209
 210        key.objectid = inode_objectid;
 211        key.offset = ref_objectid;
 212        key.type = BTRFS_INODE_REF_KEY;
 213
 214        path = btrfs_alloc_path();
 215        if (!path)
 216                return -ENOMEM;
 217
 218        path->leave_spinning = 1;
 219
 220        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
 221        if (ret > 0) {
 222                ret = -ENOENT;
 223                search_ext_refs = 1;
 224                goto out;
 225        } else if (ret < 0) {
 226                goto out;
 227        }
 228        if (!find_name_in_backref(path, name, name_len, &ref)) {
 229                ret = -ENOENT;
 230                search_ext_refs = 1;
 231                goto out;
 232        }
 233        leaf = path->nodes[0];
 234        item_size = btrfs_item_size_nr(leaf, path->slots[0]);
 235
 236        if (index)
 237                *index = btrfs_inode_ref_index(leaf, ref);
 238
 239        if (del_len == item_size) {
 240                ret = btrfs_del_item(trans, root, path);
 241                goto out;
 242        }
 243        ptr = (unsigned long)ref;
 244        sub_item_len = name_len + sizeof(*ref);
 245        item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
 246        memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
 247                              item_size - (ptr + sub_item_len - item_start));
 248        btrfs_truncate_item(root->fs_info, path, item_size - sub_item_len, 1);
 249out:
 250        btrfs_free_path(path);
 251
 252        if (search_ext_refs) {
 253                /*
 254                 * No refs were found, or we could not find the
 255                 * name in our ref array. Find and remove the extended
 256                 * inode ref then.
 257                 */
 258                return btrfs_del_inode_extref(trans, root, name, name_len,
 259                                              inode_objectid, ref_objectid, index);
 260        }
 261
 262        return ret;
 263}
 264
 265/*
 266 * btrfs_insert_inode_extref() - Inserts an extended inode ref into a tree.
 267 *
 268 * The caller must have checked against BTRFS_LINK_MAX already.
 269 */
 270static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
 271                                     struct btrfs_root *root,
 272                                     const char *name, int name_len,
 273                                     u64 inode_objectid, u64 ref_objectid, u64 index)
 274{
 275        struct btrfs_inode_extref *extref;
 276        int ret;
 277        int ins_len = name_len + sizeof(*extref);
 278        unsigned long ptr;
 279        struct btrfs_path *path;
 280        struct btrfs_key key;
 281        struct extent_buffer *leaf;
 282        struct btrfs_item *item;
 283
 284        key.objectid = inode_objectid;
 285        key.type = BTRFS_INODE_EXTREF_KEY;
 286        key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
 287
 288        path = btrfs_alloc_path();
 289        if (!path)
 290                return -ENOMEM;
 291
 292        path->leave_spinning = 1;
 293        ret = btrfs_insert_empty_item(trans, root, path, &key,
 294                                      ins_len);
 295        if (ret == -EEXIST) {
 296                if (btrfs_find_name_in_ext_backref(path, ref_objectid,
 297                                                   name, name_len, NULL))
 298                        goto out;
 299
 300                btrfs_extend_item(root->fs_info, path, ins_len);
 301                ret = 0;
 302        }
 303        if (ret < 0)
 304                goto out;
 305
 306        leaf = path->nodes[0];
 307        item = btrfs_item_nr(path->slots[0]);
 308        ptr = (unsigned long)btrfs_item_ptr(leaf, path->slots[0], char);
 309        ptr += btrfs_item_size(leaf, item) - ins_len;
 310        extref = (struct btrfs_inode_extref *)ptr;
 311
 312        btrfs_set_inode_extref_name_len(path->nodes[0], extref, name_len);
 313        btrfs_set_inode_extref_index(path->nodes[0], extref, index);
 314        btrfs_set_inode_extref_parent(path->nodes[0], extref, ref_objectid);
 315
 316        ptr = (unsigned long)&extref->name;
 317        write_extent_buffer(path->nodes[0], name, ptr, name_len);
 318        btrfs_mark_buffer_dirty(path->nodes[0]);
 319
 320out:
 321        btrfs_free_path(path);
 322        return ret;
 323}
 324
 325/* Will return 0, -ENOMEM, -EMLINK, or -EEXIST or anything from the CoW path */
 326int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
 327                           struct btrfs_root *root,
 328                           const char *name, int name_len,
 329                           u64 inode_objectid, u64 ref_objectid, u64 index)
 330{
 331        struct btrfs_fs_info *fs_info = root->fs_info;
 332        struct btrfs_path *path;
 333        struct btrfs_key key;
 334        struct btrfs_inode_ref *ref;
 335        unsigned long ptr;
 336        int ret;
 337        int ins_len = name_len + sizeof(*ref);
 338
 339        key.objectid = inode_objectid;
 340        key.offset = ref_objectid;
 341        key.type = BTRFS_INODE_REF_KEY;
 342
 343        path = btrfs_alloc_path();
 344        if (!path)
 345                return -ENOMEM;
 346
 347        path->leave_spinning = 1;
 348        path->skip_release_on_error = 1;
 349        ret = btrfs_insert_empty_item(trans, root, path, &key,
 350                                      ins_len);
 351        if (ret == -EEXIST) {
 352                u32 old_size;
 353
 354                if (find_name_in_backref(path, name, name_len, &ref))
 355                        goto out;
 356
 357                old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]);
 358                btrfs_extend_item(fs_info, path, ins_len);
 359                ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
 360                                     struct btrfs_inode_ref);
 361                ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size);
 362                btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
 363                btrfs_set_inode_ref_index(path->nodes[0], ref, index);
 364                ptr = (unsigned long)(ref + 1);
 365                ret = 0;
 366        } else if (ret < 0) {
 367                if (ret == -EOVERFLOW) {
 368                        if (find_name_in_backref(path, name, name_len, &ref))
 369                                ret = -EEXIST;
 370                        else
 371                                ret = -EMLINK;
 372                }
 373                goto out;
 374        } else {
 375                ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
 376                                     struct btrfs_inode_ref);
 377                btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
 378                btrfs_set_inode_ref_index(path->nodes[0], ref, index);
 379                ptr = (unsigned long)(ref + 1);
 380        }
 381        write_extent_buffer(path->nodes[0], name, ptr, name_len);
 382        btrfs_mark_buffer_dirty(path->nodes[0]);
 383
 384out:
 385        btrfs_free_path(path);
 386
 387        if (ret == -EMLINK) {
 388                struct btrfs_super_block *disk_super = fs_info->super_copy;
 389                /* We ran out of space in the ref array. Need to
 390                 * add an extended ref. */
 391                if (btrfs_super_incompat_flags(disk_super)
 392                    & BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
 393                        ret = btrfs_insert_inode_extref(trans, root, name,
 394                                                        name_len,
 395                                                        inode_objectid,
 396                                                        ref_objectid, index);
 397        }
 398
 399        return ret;
 400}
 401
 402int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans,
 403                             struct btrfs_root *root,
 404                             struct btrfs_path *path, u64 objectid)
 405{
 406        struct btrfs_key key;
 407        int ret;
 408        key.objectid = objectid;
 409        key.type = BTRFS_INODE_ITEM_KEY;
 410        key.offset = 0;
 411
 412        ret = btrfs_insert_empty_item(trans, root, path, &key,
 413                                      sizeof(struct btrfs_inode_item));
 414        return ret;
 415}
 416
 417int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
 418                       *root, struct btrfs_path *path,
 419                       struct btrfs_key *location, int mod)
 420{
 421        int ins_len = mod < 0 ? -1 : 0;
 422        int cow = mod != 0;
 423        int ret;
 424        int slot;
 425        struct extent_buffer *leaf;
 426        struct btrfs_key found_key;
 427
 428        ret = btrfs_search_slot(trans, root, location, path, ins_len, cow);
 429        if (ret > 0 && location->type == BTRFS_ROOT_ITEM_KEY &&
 430            location->offset == (u64)-1 && path->slots[0] != 0) {
 431                slot = path->slots[0] - 1;
 432                leaf = path->nodes[0];
 433                btrfs_item_key_to_cpu(leaf, &found_key, slot);
 434                if (found_key.objectid == location->objectid &&
 435                    found_key.type == location->type) {
 436                        path->slots[0]--;
 437                        return 0;
 438                }
 439        }
 440        return ret;
 441}
 442