Update btrfs-progs to better match the kernel
[platform/upstream/btrfs-progs.git] / dir-item.c
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 <stdio.h>
20 #include <stdlib.h>
21 #include "kerncompat.h"
22 #include "radix-tree.h"
23 #include "ctree.h"
24 #include "disk-io.h"
25 #include "hash.h"
26 #include "transaction.h"
27
28 static struct btrfs_dir_item *insert_with_overflow(struct
29                                                    btrfs_trans_handle *trans,
30                                                    struct btrfs_root *root,
31                                                    struct btrfs_path *path,
32                                                    struct btrfs_key *cpu_key,
33                                                    u32 data_size,
34                                                    const char *name,
35                                                    int name_len)
36 {
37         int ret;
38         char *ptr;
39         struct btrfs_item *item;
40         struct btrfs_leaf *leaf;
41         ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size);
42         if (ret == -EEXIST) {
43                 struct btrfs_dir_item *di;
44                 di = btrfs_match_dir_item_name(root, path, name, name_len);
45                 if (di)
46                         return NULL;
47                 ret = btrfs_extend_item(trans, root, path, data_size);
48         }
49         BUG_ON(ret > 0);
50         if (ret)
51                 return NULL;
52         leaf = &path->nodes[0]->leaf;
53         item = leaf->items + path->slots[0];
54         ptr = btrfs_item_ptr(leaf, path->slots[0], char);
55         BUG_ON(data_size > btrfs_item_size(item));
56         ptr += btrfs_item_size(item) - data_size;
57         return (struct btrfs_dir_item *)ptr;
58 }
59
60 int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
61                           *root, char *name, int name_len, u64 dir,
62                           struct btrfs_key *location, u8 type)
63 {
64         int ret = 0;
65         struct btrfs_path path;
66         struct btrfs_dir_item *dir_item;
67         char *name_ptr;
68         struct btrfs_key key;
69         u32 data_size;
70
71         key.objectid = dir;
72         btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
73         if (name_len == 1 && *name == '.')
74                 key.offset = 1;
75         else if (name_len == 2 && name[0] == '.' && name[1] == '.')
76                 key.offset = 2;
77         else
78                 ret = btrfs_name_hash(name, name_len, &key.offset);
79         BUG_ON(ret);
80         btrfs_init_path(&path);
81         data_size = sizeof(*dir_item) + name_len;
82         dir_item = insert_with_overflow(trans, root, &path, &key, data_size,
83                                         name, name_len);
84         if (!dir_item) {
85                 ret = -1;
86                 goto out;
87         }
88         btrfs_cpu_key_to_disk(&dir_item->location, location);
89         btrfs_set_dir_type(dir_item, type);
90         btrfs_set_dir_name_len(dir_item, name_len);
91         btrfs_set_dir_data_len(dir_item, 0);
92         name_ptr = (char *)(dir_item + 1);
93         memcpy(name_ptr, name, name_len);
94
95         /* FIXME, use some real flag for selecting the extra index */
96         if (root == root->fs_info->tree_root)
97                 goto out;
98
99         btrfs_release_path(root, &path);
100         btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
101         key.offset = location->objectid;
102         dir_item = insert_with_overflow(trans, root, &path, &key, data_size,
103                                         name, name_len);
104         if (!dir_item) {
105                 ret = -1;
106                 goto out;
107         }
108         btrfs_cpu_key_to_disk(&dir_item->location, location);
109         btrfs_set_dir_type(dir_item, type);
110         btrfs_set_dir_name_len(dir_item, name_len);
111         btrfs_set_dir_data_len(dir_item, 0);
112         name_ptr = (char *)(dir_item + 1);
113         memcpy(name_ptr, name, name_len);
114 out:
115         btrfs_release_path(root, &path);
116         return ret;
117 }
118 struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
119                                               struct btrfs_root *root,
120                                               struct btrfs_path *path, u64 dir,
121                                               char *name, int name_len, int mod)
122 {
123         int ret;
124         struct btrfs_key key;
125         int ins_len = mod < 0 ? -1 : 0;
126         int cow = mod != 0;
127         struct btrfs_key found_key;
128         struct btrfs_leaf *leaf;
129         key.objectid = dir;
130         btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
131         ret = btrfs_name_hash(name, name_len, &key.offset);
132         BUG_ON(ret);
133         ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
134         if (ret < 0)
135                 return NULL;
136         if (ret > 0) {
137                 if (path->slots[0] == 0)
138                         return NULL;
139                 path->slots[0]--;
140         }
141
142         leaf = &path->nodes[0]->leaf;
143         btrfs_disk_key_to_cpu(&found_key, &leaf->items[path->slots[0]].key);
144
145         if (found_key.objectid != dir ||
146             btrfs_key_type(&found_key) != BTRFS_DIR_ITEM_KEY ||
147             found_key.offset != key.offset)
148                 return NULL;
149
150         return btrfs_match_dir_item_name(root, path, name, name_len);
151 }
152
153 struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
154                               struct btrfs_path *path,
155                               const char *name, int name_len)
156 {
157         u32 cur = 0;
158         u32 this_len;
159         u32 total_len;
160         char *name_ptr;
161         struct btrfs_leaf *leaf;
162         struct btrfs_dir_item *dir_item;
163
164         leaf = &path->nodes[0]->leaf;
165         dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
166         total_len = btrfs_item_size(leaf->items + path->slots[0]);
167         while(cur < total_len) {
168                 this_len = sizeof(*dir_item) + btrfs_dir_name_len(dir_item) +
169                            btrfs_dir_data_len(dir_item);
170                 name_ptr = (char *)(dir_item + 1);
171
172                 if (btrfs_dir_name_len(dir_item) == name_len &&
173                     memcmp(name, name_ptr, name_len) == 0)
174                         return dir_item;
175
176                 cur += this_len;
177                 dir_item = (struct btrfs_dir_item *)((char *)dir_item +
178                                                      this_len);
179         }
180         return NULL;
181 }
182
183 int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
184                               struct btrfs_root *root,
185                               struct btrfs_path *path,
186                               struct btrfs_dir_item *di)
187 {
188
189         struct btrfs_leaf *leaf;
190         u32 sub_item_len;
191         u32 item_len;
192         int ret = 0;
193
194         leaf = &path->nodes[0]->leaf;
195         sub_item_len = sizeof(*di) + btrfs_dir_name_len(di) +
196                        btrfs_dir_data_len(di);
197         item_len = btrfs_item_size(leaf->items + path->slots[0]);
198         if (sub_item_len == item_len) {
199                 ret = btrfs_del_item(trans, root, path);
200         } else {
201                 char *ptr = (char *)di;
202                 char *start = btrfs_item_ptr(leaf, path->slots[0], char);
203                 memmove(ptr, ptr + sub_item_len,
204                         item_len - (ptr + sub_item_len - start));
205                 ret = btrfs_truncate_item(trans, root, path,
206                                           item_len - sub_item_len, 1);
207         }
208         return 0;
209 }