diff options
author | Boaz Harrosh <bharrosh@panasas.com> | 2008-10-28 09:38:12 -0400 |
---|---|---|
committer | Boaz Harrosh <bharrosh@panasas.com> | 2009-03-31 12:44:31 -0400 |
commit | e6af00f1d1697ca41ab6a55307066ef3466833a9 (patch) | |
tree | 7aa0b64f14a994f30e6bdc8e8c0b7f811038e794 /fs/exofs/namei.c | |
parent | beaec07ba6af35d387643b76a2920a7a6e22207b (diff) |
exofs: dir_inode and directory operations
implementation of directory and inode operations.
* A directory is treated as a file, and essentially contains a list
of <file name, inode #> pairs for files that are found in that
directory. The object IDs correspond to the files' inode numbers
and are allocated using a 64bit incrementing global counter.
* Each file's control block (AKA on-disk inode) is stored in its
object's attributes. This applies to both regular files and other
types (directories, device files, symlinks, etc.).
Signed-off-by: Boaz Harrosh <bharrosh@panasas.com>
Diffstat (limited to 'fs/exofs/namei.c')
-rw-r--r-- | fs/exofs/namei.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c new file mode 100644 index 000000000000..77fdd765e76d --- /dev/null +++ b/fs/exofs/namei.c | |||
@@ -0,0 +1,342 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005, 2006 | ||
3 | * Avishay Traeger (avishay@gmail.com) (avishay@il.ibm.com) | ||
4 | * Copyright (C) 2005, 2006 | ||
5 | * International Business Machines | ||
6 | * Copyright (C) 2008, 2009 | ||
7 | * Boaz Harrosh <bharrosh@panasas.com> | ||
8 | * | ||
9 | * Copyrights for code taken from ext2: | ||
10 | * Copyright (C) 1992, 1993, 1994, 1995 | ||
11 | * Remy Card (card@masi.ibp.fr) | ||
12 | * Laboratoire MASI - Institut Blaise Pascal | ||
13 | * Universite Pierre et Marie Curie (Paris VI) | ||
14 | * from | ||
15 | * linux/fs/minix/inode.c | ||
16 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
17 | * | ||
18 | * This file is part of exofs. | ||
19 | * | ||
20 | * exofs is free software; you can redistribute it and/or modify | ||
21 | * it under the terms of the GNU General Public License as published by | ||
22 | * the Free Software Foundation. Since it is based on ext2, and the only | ||
23 | * valid version of GPL for the Linux kernel is version 2, the only valid | ||
24 | * version of GPL for exofs is version 2. | ||
25 | * | ||
26 | * exofs is distributed in the hope that it will be useful, | ||
27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
29 | * GNU General Public License for more details. | ||
30 | * | ||
31 | * You should have received a copy of the GNU General Public License | ||
32 | * along with exofs; if not, write to the Free Software | ||
33 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
34 | */ | ||
35 | |||
36 | #include "exofs.h" | ||
37 | |||
38 | static inline int exofs_add_nondir(struct dentry *dentry, struct inode *inode) | ||
39 | { | ||
40 | int err = exofs_add_link(dentry, inode); | ||
41 | if (!err) { | ||
42 | d_instantiate(dentry, inode); | ||
43 | return 0; | ||
44 | } | ||
45 | inode_dec_link_count(inode); | ||
46 | iput(inode); | ||
47 | return err; | ||
48 | } | ||
49 | |||
50 | static struct dentry *exofs_lookup(struct inode *dir, struct dentry *dentry, | ||
51 | struct nameidata *nd) | ||
52 | { | ||
53 | struct inode *inode; | ||
54 | ino_t ino; | ||
55 | |||
56 | if (dentry->d_name.len > EXOFS_NAME_LEN) | ||
57 | return ERR_PTR(-ENAMETOOLONG); | ||
58 | |||
59 | ino = exofs_inode_by_name(dir, dentry); | ||
60 | inode = NULL; | ||
61 | if (ino) { | ||
62 | inode = exofs_iget(dir->i_sb, ino); | ||
63 | if (IS_ERR(inode)) | ||
64 | return ERR_CAST(inode); | ||
65 | } | ||
66 | return d_splice_alias(inode, dentry); | ||
67 | } | ||
68 | |||
69 | static int exofs_create(struct inode *dir, struct dentry *dentry, int mode, | ||
70 | struct nameidata *nd) | ||
71 | { | ||
72 | struct inode *inode = exofs_new_inode(dir, mode); | ||
73 | int err = PTR_ERR(inode); | ||
74 | if (!IS_ERR(inode)) { | ||
75 | inode->i_op = &exofs_file_inode_operations; | ||
76 | inode->i_fop = &exofs_file_operations; | ||
77 | inode->i_mapping->a_ops = &exofs_aops; | ||
78 | mark_inode_dirty(inode); | ||
79 | err = exofs_add_nondir(dentry, inode); | ||
80 | } | ||
81 | return err; | ||
82 | } | ||
83 | |||
84 | static int exofs_mknod(struct inode *dir, struct dentry *dentry, int mode, | ||
85 | dev_t rdev) | ||
86 | { | ||
87 | struct inode *inode; | ||
88 | int err; | ||
89 | |||
90 | if (!new_valid_dev(rdev)) | ||
91 | return -EINVAL; | ||
92 | |||
93 | inode = exofs_new_inode(dir, mode); | ||
94 | err = PTR_ERR(inode); | ||
95 | if (!IS_ERR(inode)) { | ||
96 | init_special_inode(inode, inode->i_mode, rdev); | ||
97 | mark_inode_dirty(inode); | ||
98 | err = exofs_add_nondir(dentry, inode); | ||
99 | } | ||
100 | return err; | ||
101 | } | ||
102 | |||
103 | static int exofs_symlink(struct inode *dir, struct dentry *dentry, | ||
104 | const char *symname) | ||
105 | { | ||
106 | struct super_block *sb = dir->i_sb; | ||
107 | int err = -ENAMETOOLONG; | ||
108 | unsigned l = strlen(symname)+1; | ||
109 | struct inode *inode; | ||
110 | struct exofs_i_info *oi; | ||
111 | |||
112 | if (l > sb->s_blocksize) | ||
113 | goto out; | ||
114 | |||
115 | inode = exofs_new_inode(dir, S_IFLNK | S_IRWXUGO); | ||
116 | err = PTR_ERR(inode); | ||
117 | if (IS_ERR(inode)) | ||
118 | goto out; | ||
119 | |||
120 | oi = exofs_i(inode); | ||
121 | if (l > sizeof(oi->i_data)) { | ||
122 | /* slow symlink */ | ||
123 | inode->i_op = &exofs_symlink_inode_operations; | ||
124 | inode->i_mapping->a_ops = &exofs_aops; | ||
125 | memset(oi->i_data, 0, sizeof(oi->i_data)); | ||
126 | |||
127 | err = page_symlink(inode, symname, l); | ||
128 | if (err) | ||
129 | goto out_fail; | ||
130 | } else { | ||
131 | /* fast symlink */ | ||
132 | inode->i_op = &exofs_fast_symlink_inode_operations; | ||
133 | memcpy(oi->i_data, symname, l); | ||
134 | inode->i_size = l-1; | ||
135 | } | ||
136 | mark_inode_dirty(inode); | ||
137 | |||
138 | err = exofs_add_nondir(dentry, inode); | ||
139 | out: | ||
140 | return err; | ||
141 | |||
142 | out_fail: | ||
143 | inode_dec_link_count(inode); | ||
144 | iput(inode); | ||
145 | goto out; | ||
146 | } | ||
147 | |||
148 | static int exofs_link(struct dentry *old_dentry, struct inode *dir, | ||
149 | struct dentry *dentry) | ||
150 | { | ||
151 | struct inode *inode = old_dentry->d_inode; | ||
152 | |||
153 | if (inode->i_nlink >= EXOFS_LINK_MAX) | ||
154 | return -EMLINK; | ||
155 | |||
156 | inode->i_ctime = CURRENT_TIME; | ||
157 | inode_inc_link_count(inode); | ||
158 | atomic_inc(&inode->i_count); | ||
159 | |||
160 | return exofs_add_nondir(dentry, inode); | ||
161 | } | ||
162 | |||
163 | static int exofs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
164 | { | ||
165 | struct inode *inode; | ||
166 | int err = -EMLINK; | ||
167 | |||
168 | if (dir->i_nlink >= EXOFS_LINK_MAX) | ||
169 | goto out; | ||
170 | |||
171 | inode_inc_link_count(dir); | ||
172 | |||
173 | inode = exofs_new_inode(dir, S_IFDIR | mode); | ||
174 | err = PTR_ERR(inode); | ||
175 | if (IS_ERR(inode)) | ||
176 | goto out_dir; | ||
177 | |||
178 | inode->i_op = &exofs_dir_inode_operations; | ||
179 | inode->i_fop = &exofs_dir_operations; | ||
180 | inode->i_mapping->a_ops = &exofs_aops; | ||
181 | |||
182 | inode_inc_link_count(inode); | ||
183 | |||
184 | err = exofs_make_empty(inode, dir); | ||
185 | if (err) | ||
186 | goto out_fail; | ||
187 | |||
188 | err = exofs_add_link(dentry, inode); | ||
189 | if (err) | ||
190 | goto out_fail; | ||
191 | |||
192 | d_instantiate(dentry, inode); | ||
193 | out: | ||
194 | return err; | ||
195 | |||
196 | out_fail: | ||
197 | inode_dec_link_count(inode); | ||
198 | inode_dec_link_count(inode); | ||
199 | iput(inode); | ||
200 | out_dir: | ||
201 | inode_dec_link_count(dir); | ||
202 | goto out; | ||
203 | } | ||
204 | |||
205 | static int exofs_unlink(struct inode *dir, struct dentry *dentry) | ||
206 | { | ||
207 | struct inode *inode = dentry->d_inode; | ||
208 | struct exofs_dir_entry *de; | ||
209 | struct page *page; | ||
210 | int err = -ENOENT; | ||
211 | |||
212 | de = exofs_find_entry(dir, dentry, &page); | ||
213 | if (!de) | ||
214 | goto out; | ||
215 | |||
216 | err = exofs_delete_entry(de, page); | ||
217 | if (err) | ||
218 | goto out; | ||
219 | |||
220 | inode->i_ctime = dir->i_ctime; | ||
221 | inode_dec_link_count(inode); | ||
222 | err = 0; | ||
223 | out: | ||
224 | return err; | ||
225 | } | ||
226 | |||
227 | static int exofs_rmdir(struct inode *dir, struct dentry *dentry) | ||
228 | { | ||
229 | struct inode *inode = dentry->d_inode; | ||
230 | int err = -ENOTEMPTY; | ||
231 | |||
232 | if (exofs_empty_dir(inode)) { | ||
233 | err = exofs_unlink(dir, dentry); | ||
234 | if (!err) { | ||
235 | inode->i_size = 0; | ||
236 | inode_dec_link_count(inode); | ||
237 | inode_dec_link_count(dir); | ||
238 | } | ||
239 | } | ||
240 | return err; | ||
241 | } | ||
242 | |||
243 | static int exofs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
244 | struct inode *new_dir, struct dentry *new_dentry) | ||
245 | { | ||
246 | struct inode *old_inode = old_dentry->d_inode; | ||
247 | struct inode *new_inode = new_dentry->d_inode; | ||
248 | struct page *dir_page = NULL; | ||
249 | struct exofs_dir_entry *dir_de = NULL; | ||
250 | struct page *old_page; | ||
251 | struct exofs_dir_entry *old_de; | ||
252 | int err = -ENOENT; | ||
253 | |||
254 | old_de = exofs_find_entry(old_dir, old_dentry, &old_page); | ||
255 | if (!old_de) | ||
256 | goto out; | ||
257 | |||
258 | if (S_ISDIR(old_inode->i_mode)) { | ||
259 | err = -EIO; | ||
260 | dir_de = exofs_dotdot(old_inode, &dir_page); | ||
261 | if (!dir_de) | ||
262 | goto out_old; | ||
263 | } | ||
264 | |||
265 | if (new_inode) { | ||
266 | struct page *new_page; | ||
267 | struct exofs_dir_entry *new_de; | ||
268 | |||
269 | err = -ENOTEMPTY; | ||
270 | if (dir_de && !exofs_empty_dir(new_inode)) | ||
271 | goto out_dir; | ||
272 | |||
273 | err = -ENOENT; | ||
274 | new_de = exofs_find_entry(new_dir, new_dentry, &new_page); | ||
275 | if (!new_de) | ||
276 | goto out_dir; | ||
277 | inode_inc_link_count(old_inode); | ||
278 | err = exofs_set_link(new_dir, new_de, new_page, old_inode); | ||
279 | new_inode->i_ctime = CURRENT_TIME; | ||
280 | if (dir_de) | ||
281 | drop_nlink(new_inode); | ||
282 | inode_dec_link_count(new_inode); | ||
283 | if (err) | ||
284 | goto out_dir; | ||
285 | } else { | ||
286 | if (dir_de) { | ||
287 | err = -EMLINK; | ||
288 | if (new_dir->i_nlink >= EXOFS_LINK_MAX) | ||
289 | goto out_dir; | ||
290 | } | ||
291 | inode_inc_link_count(old_inode); | ||
292 | err = exofs_add_link(new_dentry, old_inode); | ||
293 | if (err) { | ||
294 | inode_dec_link_count(old_inode); | ||
295 | goto out_dir; | ||
296 | } | ||
297 | if (dir_de) | ||
298 | inode_inc_link_count(new_dir); | ||
299 | } | ||
300 | |||
301 | old_inode->i_ctime = CURRENT_TIME; | ||
302 | |||
303 | exofs_delete_entry(old_de, old_page); | ||
304 | inode_dec_link_count(old_inode); | ||
305 | |||
306 | if (dir_de) { | ||
307 | err = exofs_set_link(old_inode, dir_de, dir_page, new_dir); | ||
308 | inode_dec_link_count(old_dir); | ||
309 | if (err) | ||
310 | goto out_dir; | ||
311 | } | ||
312 | return 0; | ||
313 | |||
314 | |||
315 | out_dir: | ||
316 | if (dir_de) { | ||
317 | kunmap(dir_page); | ||
318 | page_cache_release(dir_page); | ||
319 | } | ||
320 | out_old: | ||
321 | kunmap(old_page); | ||
322 | page_cache_release(old_page); | ||
323 | out: | ||
324 | return err; | ||
325 | } | ||
326 | |||
327 | const struct inode_operations exofs_dir_inode_operations = { | ||
328 | .create = exofs_create, | ||
329 | .lookup = exofs_lookup, | ||
330 | .link = exofs_link, | ||
331 | .unlink = exofs_unlink, | ||
332 | .symlink = exofs_symlink, | ||
333 | .mkdir = exofs_mkdir, | ||
334 | .rmdir = exofs_rmdir, | ||
335 | .mknod = exofs_mknod, | ||
336 | .rename = exofs_rename, | ||
337 | .setattr = exofs_setattr, | ||
338 | }; | ||
339 | |||
340 | const struct inode_operations exofs_special_inode_operations = { | ||
341 | .setattr = exofs_setattr, | ||
342 | }; | ||