Commit e6af00f1d1697ca41ab6a55307066ef3466833a9
1 parent
beaec07ba6
Exists in
master
and in
7 other branches
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>
Showing 5 changed files with 1298 additions and 1 deletions Side-by-side Diff
fs/exofs/Kbuild
fs/exofs/dir.c
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 unsigned exofs_chunk_size(struct inode *inode) | |
39 | +{ | |
40 | + return inode->i_sb->s_blocksize; | |
41 | +} | |
42 | + | |
43 | +static inline void exofs_put_page(struct page *page) | |
44 | +{ | |
45 | + kunmap(page); | |
46 | + page_cache_release(page); | |
47 | +} | |
48 | + | |
49 | +/* Accesses dir's inode->i_size must be called under inode lock */ | |
50 | +static inline unsigned long dir_pages(struct inode *inode) | |
51 | +{ | |
52 | + return (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | |
53 | +} | |
54 | + | |
55 | +static unsigned exofs_last_byte(struct inode *inode, unsigned long page_nr) | |
56 | +{ | |
57 | + loff_t last_byte = inode->i_size; | |
58 | + | |
59 | + last_byte -= page_nr << PAGE_CACHE_SHIFT; | |
60 | + if (last_byte > PAGE_CACHE_SIZE) | |
61 | + last_byte = PAGE_CACHE_SIZE; | |
62 | + return last_byte; | |
63 | +} | |
64 | + | |
65 | +static int exofs_commit_chunk(struct page *page, loff_t pos, unsigned len) | |
66 | +{ | |
67 | + struct address_space *mapping = page->mapping; | |
68 | + struct inode *dir = mapping->host; | |
69 | + int err = 0; | |
70 | + | |
71 | + dir->i_version++; | |
72 | + | |
73 | + if (!PageUptodate(page)) | |
74 | + SetPageUptodate(page); | |
75 | + | |
76 | + if (pos+len > dir->i_size) { | |
77 | + i_size_write(dir, pos+len); | |
78 | + mark_inode_dirty(dir); | |
79 | + } | |
80 | + set_page_dirty(page); | |
81 | + | |
82 | + if (IS_DIRSYNC(dir)) | |
83 | + err = write_one_page(page, 1); | |
84 | + else | |
85 | + unlock_page(page); | |
86 | + | |
87 | + return err; | |
88 | +} | |
89 | + | |
90 | +static void exofs_check_page(struct page *page) | |
91 | +{ | |
92 | + struct inode *dir = page->mapping->host; | |
93 | + unsigned chunk_size = exofs_chunk_size(dir); | |
94 | + char *kaddr = page_address(page); | |
95 | + unsigned offs, rec_len; | |
96 | + unsigned limit = PAGE_CACHE_SIZE; | |
97 | + struct exofs_dir_entry *p; | |
98 | + char *error; | |
99 | + | |
100 | + /* if the page is the last one in the directory */ | |
101 | + if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { | |
102 | + limit = dir->i_size & ~PAGE_CACHE_MASK; | |
103 | + if (limit & (chunk_size - 1)) | |
104 | + goto Ebadsize; | |
105 | + if (!limit) | |
106 | + goto out; | |
107 | + } | |
108 | + for (offs = 0; offs <= limit - EXOFS_DIR_REC_LEN(1); offs += rec_len) { | |
109 | + p = (struct exofs_dir_entry *)(kaddr + offs); | |
110 | + rec_len = le16_to_cpu(p->rec_len); | |
111 | + | |
112 | + if (rec_len < EXOFS_DIR_REC_LEN(1)) | |
113 | + goto Eshort; | |
114 | + if (rec_len & 3) | |
115 | + goto Ealign; | |
116 | + if (rec_len < EXOFS_DIR_REC_LEN(p->name_len)) | |
117 | + goto Enamelen; | |
118 | + if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) | |
119 | + goto Espan; | |
120 | + } | |
121 | + if (offs != limit) | |
122 | + goto Eend; | |
123 | +out: | |
124 | + SetPageChecked(page); | |
125 | + return; | |
126 | + | |
127 | +Ebadsize: | |
128 | + EXOFS_ERR("ERROR [exofs_check_page]: " | |
129 | + "size of directory #%lu is not a multiple of chunk size", | |
130 | + dir->i_ino | |
131 | + ); | |
132 | + goto fail; | |
133 | +Eshort: | |
134 | + error = "rec_len is smaller than minimal"; | |
135 | + goto bad_entry; | |
136 | +Ealign: | |
137 | + error = "unaligned directory entry"; | |
138 | + goto bad_entry; | |
139 | +Enamelen: | |
140 | + error = "rec_len is too small for name_len"; | |
141 | + goto bad_entry; | |
142 | +Espan: | |
143 | + error = "directory entry across blocks"; | |
144 | + goto bad_entry; | |
145 | +bad_entry: | |
146 | + EXOFS_ERR( | |
147 | + "ERROR [exofs_check_page]: bad entry in directory #%lu: %s - " | |
148 | + "offset=%lu, inode=%llu, rec_len=%d, name_len=%d", | |
149 | + dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, | |
150 | + _LLU(le64_to_cpu(p->inode_no)), | |
151 | + rec_len, p->name_len); | |
152 | + goto fail; | |
153 | +Eend: | |
154 | + p = (struct exofs_dir_entry *)(kaddr + offs); | |
155 | + EXOFS_ERR("ERROR [exofs_check_page]: " | |
156 | + "entry in directory #%lu spans the page boundary" | |
157 | + "offset=%lu, inode=%llu", | |
158 | + dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, | |
159 | + _LLU(le64_to_cpu(p->inode_no))); | |
160 | +fail: | |
161 | + SetPageChecked(page); | |
162 | + SetPageError(page); | |
163 | +} | |
164 | + | |
165 | +static struct page *exofs_get_page(struct inode *dir, unsigned long n) | |
166 | +{ | |
167 | + struct address_space *mapping = dir->i_mapping; | |
168 | + struct page *page = read_mapping_page(mapping, n, NULL); | |
169 | + | |
170 | + if (!IS_ERR(page)) { | |
171 | + kmap(page); | |
172 | + if (!PageChecked(page)) | |
173 | + exofs_check_page(page); | |
174 | + if (PageError(page)) | |
175 | + goto fail; | |
176 | + } | |
177 | + return page; | |
178 | + | |
179 | +fail: | |
180 | + exofs_put_page(page); | |
181 | + return ERR_PTR(-EIO); | |
182 | +} | |
183 | + | |
184 | +static inline int exofs_match(int len, const unsigned char *name, | |
185 | + struct exofs_dir_entry *de) | |
186 | +{ | |
187 | + if (len != de->name_len) | |
188 | + return 0; | |
189 | + if (!de->inode_no) | |
190 | + return 0; | |
191 | + return !memcmp(name, de->name, len); | |
192 | +} | |
193 | + | |
194 | +static inline | |
195 | +struct exofs_dir_entry *exofs_next_entry(struct exofs_dir_entry *p) | |
196 | +{ | |
197 | + return (struct exofs_dir_entry *)((char *)p + le16_to_cpu(p->rec_len)); | |
198 | +} | |
199 | + | |
200 | +static inline unsigned | |
201 | +exofs_validate_entry(char *base, unsigned offset, unsigned mask) | |
202 | +{ | |
203 | + struct exofs_dir_entry *de = (struct exofs_dir_entry *)(base + offset); | |
204 | + struct exofs_dir_entry *p = | |
205 | + (struct exofs_dir_entry *)(base + (offset&mask)); | |
206 | + while ((char *)p < (char *)de) { | |
207 | + if (p->rec_len == 0) | |
208 | + break; | |
209 | + p = exofs_next_entry(p); | |
210 | + } | |
211 | + return (char *)p - base; | |
212 | +} | |
213 | + | |
214 | +static unsigned char exofs_filetype_table[EXOFS_FT_MAX] = { | |
215 | + [EXOFS_FT_UNKNOWN] = DT_UNKNOWN, | |
216 | + [EXOFS_FT_REG_FILE] = DT_REG, | |
217 | + [EXOFS_FT_DIR] = DT_DIR, | |
218 | + [EXOFS_FT_CHRDEV] = DT_CHR, | |
219 | + [EXOFS_FT_BLKDEV] = DT_BLK, | |
220 | + [EXOFS_FT_FIFO] = DT_FIFO, | |
221 | + [EXOFS_FT_SOCK] = DT_SOCK, | |
222 | + [EXOFS_FT_SYMLINK] = DT_LNK, | |
223 | +}; | |
224 | + | |
225 | +#define S_SHIFT 12 | |
226 | +static unsigned char exofs_type_by_mode[S_IFMT >> S_SHIFT] = { | |
227 | + [S_IFREG >> S_SHIFT] = EXOFS_FT_REG_FILE, | |
228 | + [S_IFDIR >> S_SHIFT] = EXOFS_FT_DIR, | |
229 | + [S_IFCHR >> S_SHIFT] = EXOFS_FT_CHRDEV, | |
230 | + [S_IFBLK >> S_SHIFT] = EXOFS_FT_BLKDEV, | |
231 | + [S_IFIFO >> S_SHIFT] = EXOFS_FT_FIFO, | |
232 | + [S_IFSOCK >> S_SHIFT] = EXOFS_FT_SOCK, | |
233 | + [S_IFLNK >> S_SHIFT] = EXOFS_FT_SYMLINK, | |
234 | +}; | |
235 | + | |
236 | +static inline | |
237 | +void exofs_set_de_type(struct exofs_dir_entry *de, struct inode *inode) | |
238 | +{ | |
239 | + mode_t mode = inode->i_mode; | |
240 | + de->file_type = exofs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; | |
241 | +} | |
242 | + | |
243 | +static int | |
244 | +exofs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |
245 | +{ | |
246 | + loff_t pos = filp->f_pos; | |
247 | + struct inode *inode = filp->f_path.dentry->d_inode; | |
248 | + unsigned int offset = pos & ~PAGE_CACHE_MASK; | |
249 | + unsigned long n = pos >> PAGE_CACHE_SHIFT; | |
250 | + unsigned long npages = dir_pages(inode); | |
251 | + unsigned chunk_mask = ~(exofs_chunk_size(inode)-1); | |
252 | + unsigned char *types = NULL; | |
253 | + int need_revalidate = (filp->f_version != inode->i_version); | |
254 | + | |
255 | + if (pos > inode->i_size - EXOFS_DIR_REC_LEN(1)) | |
256 | + return 0; | |
257 | + | |
258 | + types = exofs_filetype_table; | |
259 | + | |
260 | + for ( ; n < npages; n++, offset = 0) { | |
261 | + char *kaddr, *limit; | |
262 | + struct exofs_dir_entry *de; | |
263 | + struct page *page = exofs_get_page(inode, n); | |
264 | + | |
265 | + if (IS_ERR(page)) { | |
266 | + EXOFS_ERR("ERROR: " | |
267 | + "bad page in #%lu", | |
268 | + inode->i_ino); | |
269 | + filp->f_pos += PAGE_CACHE_SIZE - offset; | |
270 | + return PTR_ERR(page); | |
271 | + } | |
272 | + kaddr = page_address(page); | |
273 | + if (unlikely(need_revalidate)) { | |
274 | + if (offset) { | |
275 | + offset = exofs_validate_entry(kaddr, offset, | |
276 | + chunk_mask); | |
277 | + filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset; | |
278 | + } | |
279 | + filp->f_version = inode->i_version; | |
280 | + need_revalidate = 0; | |
281 | + } | |
282 | + de = (struct exofs_dir_entry *)(kaddr + offset); | |
283 | + limit = kaddr + exofs_last_byte(inode, n) - | |
284 | + EXOFS_DIR_REC_LEN(1); | |
285 | + for (; (char *)de <= limit; de = exofs_next_entry(de)) { | |
286 | + if (de->rec_len == 0) { | |
287 | + EXOFS_ERR("ERROR: " | |
288 | + "zero-length directory entry"); | |
289 | + exofs_put_page(page); | |
290 | + return -EIO; | |
291 | + } | |
292 | + if (de->inode_no) { | |
293 | + int over; | |
294 | + unsigned char d_type = DT_UNKNOWN; | |
295 | + | |
296 | + if (types && de->file_type < EXOFS_FT_MAX) | |
297 | + d_type = types[de->file_type]; | |
298 | + | |
299 | + offset = (char *)de - kaddr; | |
300 | + over = filldir(dirent, de->name, de->name_len, | |
301 | + (n<<PAGE_CACHE_SHIFT) | offset, | |
302 | + le64_to_cpu(de->inode_no), | |
303 | + d_type); | |
304 | + if (over) { | |
305 | + exofs_put_page(page); | |
306 | + return 0; | |
307 | + } | |
308 | + } | |
309 | + filp->f_pos += le16_to_cpu(de->rec_len); | |
310 | + } | |
311 | + exofs_put_page(page); | |
312 | + } | |
313 | + | |
314 | + return 0; | |
315 | +} | |
316 | + | |
317 | +struct exofs_dir_entry *exofs_find_entry(struct inode *dir, | |
318 | + struct dentry *dentry, struct page **res_page) | |
319 | +{ | |
320 | + const unsigned char *name = dentry->d_name.name; | |
321 | + int namelen = dentry->d_name.len; | |
322 | + unsigned reclen = EXOFS_DIR_REC_LEN(namelen); | |
323 | + unsigned long start, n; | |
324 | + unsigned long npages = dir_pages(dir); | |
325 | + struct page *page = NULL; | |
326 | + struct exofs_i_info *oi = exofs_i(dir); | |
327 | + struct exofs_dir_entry *de; | |
328 | + | |
329 | + if (npages == 0) | |
330 | + goto out; | |
331 | + | |
332 | + *res_page = NULL; | |
333 | + | |
334 | + start = oi->i_dir_start_lookup; | |
335 | + if (start >= npages) | |
336 | + start = 0; | |
337 | + n = start; | |
338 | + do { | |
339 | + char *kaddr; | |
340 | + page = exofs_get_page(dir, n); | |
341 | + if (!IS_ERR(page)) { | |
342 | + kaddr = page_address(page); | |
343 | + de = (struct exofs_dir_entry *) kaddr; | |
344 | + kaddr += exofs_last_byte(dir, n) - reclen; | |
345 | + while ((char *) de <= kaddr) { | |
346 | + if (de->rec_len == 0) { | |
347 | + EXOFS_ERR( | |
348 | + "ERROR: exofs_find_entry: " | |
349 | + "zero-length directory entry"); | |
350 | + exofs_put_page(page); | |
351 | + goto out; | |
352 | + } | |
353 | + if (exofs_match(namelen, name, de)) | |
354 | + goto found; | |
355 | + de = exofs_next_entry(de); | |
356 | + } | |
357 | + exofs_put_page(page); | |
358 | + } | |
359 | + if (++n >= npages) | |
360 | + n = 0; | |
361 | + } while (n != start); | |
362 | +out: | |
363 | + return NULL; | |
364 | + | |
365 | +found: | |
366 | + *res_page = page; | |
367 | + oi->i_dir_start_lookup = n; | |
368 | + return de; | |
369 | +} | |
370 | + | |
371 | +struct exofs_dir_entry *exofs_dotdot(struct inode *dir, struct page **p) | |
372 | +{ | |
373 | + struct page *page = exofs_get_page(dir, 0); | |
374 | + struct exofs_dir_entry *de = NULL; | |
375 | + | |
376 | + if (!IS_ERR(page)) { | |
377 | + de = exofs_next_entry( | |
378 | + (struct exofs_dir_entry *)page_address(page)); | |
379 | + *p = page; | |
380 | + } | |
381 | + return de; | |
382 | +} | |
383 | + | |
384 | +ino_t exofs_inode_by_name(struct inode *dir, struct dentry *dentry) | |
385 | +{ | |
386 | + ino_t res = 0; | |
387 | + struct exofs_dir_entry *de; | |
388 | + struct page *page; | |
389 | + | |
390 | + de = exofs_find_entry(dir, dentry, &page); | |
391 | + if (de) { | |
392 | + res = le64_to_cpu(de->inode_no); | |
393 | + exofs_put_page(page); | |
394 | + } | |
395 | + return res; | |
396 | +} | |
397 | + | |
398 | +int exofs_set_link(struct inode *dir, struct exofs_dir_entry *de, | |
399 | + struct page *page, struct inode *inode) | |
400 | +{ | |
401 | + loff_t pos = page_offset(page) + | |
402 | + (char *) de - (char *) page_address(page); | |
403 | + unsigned len = le16_to_cpu(de->rec_len); | |
404 | + int err; | |
405 | + | |
406 | + lock_page(page); | |
407 | + err = exofs_write_begin(NULL, page->mapping, pos, len, | |
408 | + AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); | |
409 | + if (err) | |
410 | + EXOFS_ERR("exofs_set_link: exofs_write_begin FAILD => %d\n", | |
411 | + err); | |
412 | + | |
413 | + de->inode_no = cpu_to_le64(inode->i_ino); | |
414 | + exofs_set_de_type(de, inode); | |
415 | + if (likely(!err)) | |
416 | + err = exofs_commit_chunk(page, pos, len); | |
417 | + exofs_put_page(page); | |
418 | + dir->i_mtime = dir->i_ctime = CURRENT_TIME; | |
419 | + mark_inode_dirty(dir); | |
420 | + return err; | |
421 | +} | |
422 | + | |
423 | +int exofs_add_link(struct dentry *dentry, struct inode *inode) | |
424 | +{ | |
425 | + struct inode *dir = dentry->d_parent->d_inode; | |
426 | + const unsigned char *name = dentry->d_name.name; | |
427 | + int namelen = dentry->d_name.len; | |
428 | + unsigned chunk_size = exofs_chunk_size(dir); | |
429 | + unsigned reclen = EXOFS_DIR_REC_LEN(namelen); | |
430 | + unsigned short rec_len, name_len; | |
431 | + struct page *page = NULL; | |
432 | + struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; | |
433 | + struct exofs_dir_entry *de; | |
434 | + unsigned long npages = dir_pages(dir); | |
435 | + unsigned long n; | |
436 | + char *kaddr; | |
437 | + loff_t pos; | |
438 | + int err; | |
439 | + | |
440 | + for (n = 0; n <= npages; n++) { | |
441 | + char *dir_end; | |
442 | + | |
443 | + page = exofs_get_page(dir, n); | |
444 | + err = PTR_ERR(page); | |
445 | + if (IS_ERR(page)) | |
446 | + goto out; | |
447 | + lock_page(page); | |
448 | + kaddr = page_address(page); | |
449 | + dir_end = kaddr + exofs_last_byte(dir, n); | |
450 | + de = (struct exofs_dir_entry *)kaddr; | |
451 | + kaddr += PAGE_CACHE_SIZE - reclen; | |
452 | + while ((char *)de <= kaddr) { | |
453 | + if ((char *)de == dir_end) { | |
454 | + name_len = 0; | |
455 | + rec_len = chunk_size; | |
456 | + de->rec_len = cpu_to_le16(chunk_size); | |
457 | + de->inode_no = 0; | |
458 | + goto got_it; | |
459 | + } | |
460 | + if (de->rec_len == 0) { | |
461 | + EXOFS_ERR("ERROR: exofs_add_link: " | |
462 | + "zero-length directory entry"); | |
463 | + err = -EIO; | |
464 | + goto out_unlock; | |
465 | + } | |
466 | + err = -EEXIST; | |
467 | + if (exofs_match(namelen, name, de)) | |
468 | + goto out_unlock; | |
469 | + name_len = EXOFS_DIR_REC_LEN(de->name_len); | |
470 | + rec_len = le16_to_cpu(de->rec_len); | |
471 | + if (!de->inode_no && rec_len >= reclen) | |
472 | + goto got_it; | |
473 | + if (rec_len >= name_len + reclen) | |
474 | + goto got_it; | |
475 | + de = (struct exofs_dir_entry *) ((char *) de + rec_len); | |
476 | + } | |
477 | + unlock_page(page); | |
478 | + exofs_put_page(page); | |
479 | + } | |
480 | + | |
481 | + EXOFS_ERR("exofs_add_link: BAD dentry=%p or inode=%p", dentry, inode); | |
482 | + return -EINVAL; | |
483 | + | |
484 | +got_it: | |
485 | + pos = page_offset(page) + | |
486 | + (char *)de - (char *)page_address(page); | |
487 | + err = exofs_write_begin(NULL, page->mapping, pos, rec_len, 0, | |
488 | + &page, NULL); | |
489 | + if (err) | |
490 | + goto out_unlock; | |
491 | + if (de->inode_no) { | |
492 | + struct exofs_dir_entry *de1 = | |
493 | + (struct exofs_dir_entry *)((char *)de + name_len); | |
494 | + de1->rec_len = cpu_to_le16(rec_len - name_len); | |
495 | + de->rec_len = cpu_to_le16(name_len); | |
496 | + de = de1; | |
497 | + } | |
498 | + de->name_len = namelen; | |
499 | + memcpy(de->name, name, namelen); | |
500 | + de->inode_no = cpu_to_le64(inode->i_ino); | |
501 | + exofs_set_de_type(de, inode); | |
502 | + err = exofs_commit_chunk(page, pos, rec_len); | |
503 | + dir->i_mtime = dir->i_ctime = CURRENT_TIME; | |
504 | + mark_inode_dirty(dir); | |
505 | + sbi->s_numfiles++; | |
506 | + | |
507 | +out_put: | |
508 | + exofs_put_page(page); | |
509 | +out: | |
510 | + return err; | |
511 | +out_unlock: | |
512 | + unlock_page(page); | |
513 | + goto out_put; | |
514 | +} | |
515 | + | |
516 | +int exofs_delete_entry(struct exofs_dir_entry *dir, struct page *page) | |
517 | +{ | |
518 | + struct address_space *mapping = page->mapping; | |
519 | + struct inode *inode = mapping->host; | |
520 | + struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; | |
521 | + char *kaddr = page_address(page); | |
522 | + unsigned from = ((char *)dir - kaddr) & ~(exofs_chunk_size(inode)-1); | |
523 | + unsigned to = ((char *)dir - kaddr) + le16_to_cpu(dir->rec_len); | |
524 | + loff_t pos; | |
525 | + struct exofs_dir_entry *pde = NULL; | |
526 | + struct exofs_dir_entry *de = (struct exofs_dir_entry *) (kaddr + from); | |
527 | + int err; | |
528 | + | |
529 | + while (de < dir) { | |
530 | + if (de->rec_len == 0) { | |
531 | + EXOFS_ERR("ERROR: exofs_delete_entry:" | |
532 | + "zero-length directory entry"); | |
533 | + err = -EIO; | |
534 | + goto out; | |
535 | + } | |
536 | + pde = de; | |
537 | + de = exofs_next_entry(de); | |
538 | + } | |
539 | + if (pde) | |
540 | + from = (char *)pde - (char *)page_address(page); | |
541 | + pos = page_offset(page) + from; | |
542 | + lock_page(page); | |
543 | + err = exofs_write_begin(NULL, page->mapping, pos, to - from, 0, | |
544 | + &page, NULL); | |
545 | + if (err) | |
546 | + EXOFS_ERR("exofs_delete_entry: exofs_write_begin FAILD => %d\n", | |
547 | + err); | |
548 | + if (pde) | |
549 | + pde->rec_len = cpu_to_le16(to - from); | |
550 | + dir->inode_no = 0; | |
551 | + if (likely(!err)) | |
552 | + err = exofs_commit_chunk(page, pos, to - from); | |
553 | + inode->i_ctime = inode->i_mtime = CURRENT_TIME; | |
554 | + mark_inode_dirty(inode); | |
555 | + sbi->s_numfiles--; | |
556 | +out: | |
557 | + exofs_put_page(page); | |
558 | + return err; | |
559 | +} | |
560 | + | |
561 | +/* kept aligned on 4 bytes */ | |
562 | +#define THIS_DIR ".\0\0" | |
563 | +#define PARENT_DIR "..\0" | |
564 | + | |
565 | +int exofs_make_empty(struct inode *inode, struct inode *parent) | |
566 | +{ | |
567 | + struct address_space *mapping = inode->i_mapping; | |
568 | + struct page *page = grab_cache_page(mapping, 0); | |
569 | + unsigned chunk_size = exofs_chunk_size(inode); | |
570 | + struct exofs_dir_entry *de; | |
571 | + int err; | |
572 | + void *kaddr; | |
573 | + | |
574 | + if (!page) | |
575 | + return -ENOMEM; | |
576 | + | |
577 | + err = exofs_write_begin(NULL, page->mapping, 0, chunk_size, 0, | |
578 | + &page, NULL); | |
579 | + if (err) { | |
580 | + unlock_page(page); | |
581 | + goto fail; | |
582 | + } | |
583 | + | |
584 | + kaddr = kmap_atomic(page, KM_USER0); | |
585 | + de = (struct exofs_dir_entry *)kaddr; | |
586 | + de->name_len = 1; | |
587 | + de->rec_len = cpu_to_le16(EXOFS_DIR_REC_LEN(1)); | |
588 | + memcpy(de->name, THIS_DIR, sizeof(THIS_DIR)); | |
589 | + de->inode_no = cpu_to_le64(inode->i_ino); | |
590 | + exofs_set_de_type(de, inode); | |
591 | + | |
592 | + de = (struct exofs_dir_entry *)(kaddr + EXOFS_DIR_REC_LEN(1)); | |
593 | + de->name_len = 2; | |
594 | + de->rec_len = cpu_to_le16(chunk_size - EXOFS_DIR_REC_LEN(1)); | |
595 | + de->inode_no = cpu_to_le64(parent->i_ino); | |
596 | + memcpy(de->name, PARENT_DIR, sizeof(PARENT_DIR)); | |
597 | + exofs_set_de_type(de, inode); | |
598 | + kunmap_atomic(page, KM_USER0); | |
599 | + err = exofs_commit_chunk(page, 0, chunk_size); | |
600 | +fail: | |
601 | + page_cache_release(page); | |
602 | + return err; | |
603 | +} | |
604 | + | |
605 | +int exofs_empty_dir(struct inode *inode) | |
606 | +{ | |
607 | + struct page *page = NULL; | |
608 | + unsigned long i, npages = dir_pages(inode); | |
609 | + | |
610 | + for (i = 0; i < npages; i++) { | |
611 | + char *kaddr; | |
612 | + struct exofs_dir_entry *de; | |
613 | + page = exofs_get_page(inode, i); | |
614 | + | |
615 | + if (IS_ERR(page)) | |
616 | + continue; | |
617 | + | |
618 | + kaddr = page_address(page); | |
619 | + de = (struct exofs_dir_entry *)kaddr; | |
620 | + kaddr += exofs_last_byte(inode, i) - EXOFS_DIR_REC_LEN(1); | |
621 | + | |
622 | + while ((char *)de <= kaddr) { | |
623 | + if (de->rec_len == 0) { | |
624 | + EXOFS_ERR("ERROR: exofs_empty_dir: " | |
625 | + "zero-length directory entry" | |
626 | + "kaddr=%p, de=%p\n", kaddr, de); | |
627 | + goto not_empty; | |
628 | + } | |
629 | + if (de->inode_no != 0) { | |
630 | + /* check for . and .. */ | |
631 | + if (de->name[0] != '.') | |
632 | + goto not_empty; | |
633 | + if (de->name_len > 2) | |
634 | + goto not_empty; | |
635 | + if (de->name_len < 2) { | |
636 | + if (le64_to_cpu(de->inode_no) != | |
637 | + inode->i_ino) | |
638 | + goto not_empty; | |
639 | + } else if (de->name[1] != '.') | |
640 | + goto not_empty; | |
641 | + } | |
642 | + de = exofs_next_entry(de); | |
643 | + } | |
644 | + exofs_put_page(page); | |
645 | + } | |
646 | + return 1; | |
647 | + | |
648 | +not_empty: | |
649 | + exofs_put_page(page); | |
650 | + return 0; | |
651 | +} | |
652 | + | |
653 | +const struct file_operations exofs_dir_operations = { | |
654 | + .llseek = generic_file_llseek, | |
655 | + .read = generic_read_dir, | |
656 | + .readdir = exofs_readdir, | |
657 | +}; |
fs/exofs/exofs.h
... | ... | @@ -124,6 +124,11 @@ |
124 | 124 | return container_of(inode, struct exofs_i_info, vfs_inode); |
125 | 125 | } |
126 | 126 | |
127 | +/* | |
128 | + * Maximum count of links to a file | |
129 | + */ | |
130 | +#define EXOFS_LINK_MAX 32000 | |
131 | + | |
127 | 132 | /************************* |
128 | 133 | * function declarations * |
129 | 134 | *************************/ |
130 | 135 | |
131 | 136 | |
132 | 137 | |
... | ... | @@ -133,16 +138,37 @@ |
133 | 138 | int exofs_write_begin(struct file *file, struct address_space *mapping, |
134 | 139 | loff_t pos, unsigned len, unsigned flags, |
135 | 140 | struct page **pagep, void **fsdata); |
141 | +extern struct inode *exofs_iget(struct super_block *, unsigned long); | |
142 | +struct inode *exofs_new_inode(struct inode *, int); | |
136 | 143 | |
144 | +/* dir.c: */ | |
145 | +int exofs_add_link(struct dentry *, struct inode *); | |
146 | +ino_t exofs_inode_by_name(struct inode *, struct dentry *); | |
147 | +int exofs_delete_entry(struct exofs_dir_entry *, struct page *); | |
148 | +int exofs_make_empty(struct inode *, struct inode *); | |
149 | +struct exofs_dir_entry *exofs_find_entry(struct inode *, struct dentry *, | |
150 | + struct page **); | |
151 | +int exofs_empty_dir(struct inode *); | |
152 | +struct exofs_dir_entry *exofs_dotdot(struct inode *, struct page **); | |
153 | +int exofs_set_link(struct inode *, struct exofs_dir_entry *, struct page *, | |
154 | + struct inode *); | |
155 | + | |
137 | 156 | /********************* |
138 | 157 | * operation vectors * |
139 | 158 | *********************/ |
159 | +/* dir.c: */ | |
160 | +extern const struct file_operations exofs_dir_operations; | |
161 | + | |
140 | 162 | /* file.c */ |
141 | 163 | extern const struct inode_operations exofs_file_inode_operations; |
142 | 164 | extern const struct file_operations exofs_file_operations; |
143 | 165 | |
144 | 166 | /* inode.c */ |
145 | 167 | extern const struct address_space_operations exofs_aops; |
168 | + | |
169 | +/* namei.c */ | |
170 | +extern const struct inode_operations exofs_dir_inode_operations; | |
171 | +extern const struct inode_operations exofs_special_inode_operations; | |
146 | 172 | |
147 | 173 | /* symlink.c */ |
148 | 174 | extern const struct inode_operations exofs_symlink_inode_operations; |
fs/exofs/inode.c
... | ... | @@ -843,4 +843,276 @@ |
843 | 843 | error = inode_setattr(inode, iattr); |
844 | 844 | return error; |
845 | 845 | } |
846 | + | |
847 | +/* | |
848 | + * Read an inode from the OSD, and return it as is. We also return the size | |
849 | + * attribute in the 'sanity' argument if we got compiled with debugging turned | |
850 | + * on. | |
851 | + */ | |
852 | +static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi, | |
853 | + struct exofs_fcb *inode, uint64_t *sanity) | |
854 | +{ | |
855 | + struct exofs_sb_info *sbi = sb->s_fs_info; | |
856 | + struct osd_request *or; | |
857 | + struct osd_attr attr; | |
858 | + struct osd_obj_id obj = {sbi->s_pid, | |
859 | + oi->vfs_inode.i_ino + EXOFS_OBJ_OFF}; | |
860 | + int ret; | |
861 | + | |
862 | + exofs_make_credential(oi->i_cred, &obj); | |
863 | + | |
864 | + or = osd_start_request(sbi->s_dev, GFP_KERNEL); | |
865 | + if (unlikely(!or)) { | |
866 | + EXOFS_ERR("exofs_get_inode: osd_start_request failed.\n"); | |
867 | + return -ENOMEM; | |
868 | + } | |
869 | + osd_req_get_attributes(or, &obj); | |
870 | + | |
871 | + /* we need the inode attribute */ | |
872 | + osd_req_add_get_attr_list(or, &g_attr_inode_data, 1); | |
873 | + | |
874 | +#ifdef EXOFS_DEBUG_OBJ_ISIZE | |
875 | + /* we get the size attributes to do a sanity check */ | |
876 | + osd_req_add_get_attr_list(or, &g_attr_logical_length, 1); | |
877 | +#endif | |
878 | + | |
879 | + ret = exofs_sync_op(or, sbi->s_timeout, oi->i_cred); | |
880 | + if (ret) | |
881 | + goto out; | |
882 | + | |
883 | + attr = g_attr_inode_data; | |
884 | + ret = extract_attr_from_req(or, &attr); | |
885 | + if (ret) { | |
886 | + EXOFS_ERR("exofs_get_inode: extract_attr_from_req failed\n"); | |
887 | + goto out; | |
888 | + } | |
889 | + | |
890 | + WARN_ON(attr.len != EXOFS_INO_ATTR_SIZE); | |
891 | + memcpy(inode, attr.val_ptr, EXOFS_INO_ATTR_SIZE); | |
892 | + | |
893 | +#ifdef EXOFS_DEBUG_OBJ_ISIZE | |
894 | + attr = g_attr_logical_length; | |
895 | + ret = extract_attr_from_req(or, &attr); | |
896 | + if (ret) { | |
897 | + EXOFS_ERR("ERROR: extract attr from or failed\n"); | |
898 | + goto out; | |
899 | + } | |
900 | + *sanity = get_unaligned_be64(attr.val_ptr); | |
901 | +#endif | |
902 | + | |
903 | +out: | |
904 | + osd_end_request(or); | |
905 | + return ret; | |
906 | +} | |
907 | + | |
908 | +/* | |
909 | + * Fill in an inode read from the OSD and set it up for use | |
910 | + */ | |
911 | +struct inode *exofs_iget(struct super_block *sb, unsigned long ino) | |
912 | +{ | |
913 | + struct exofs_i_info *oi; | |
914 | + struct exofs_fcb fcb; | |
915 | + struct inode *inode; | |
916 | + uint64_t uninitialized_var(sanity); | |
917 | + int ret; | |
918 | + | |
919 | + inode = iget_locked(sb, ino); | |
920 | + if (!inode) | |
921 | + return ERR_PTR(-ENOMEM); | |
922 | + if (!(inode->i_state & I_NEW)) | |
923 | + return inode; | |
924 | + oi = exofs_i(inode); | |
925 | + | |
926 | + /* read the inode from the osd */ | |
927 | + ret = exofs_get_inode(sb, oi, &fcb, &sanity); | |
928 | + if (ret) | |
929 | + goto bad_inode; | |
930 | + | |
931 | + init_waitqueue_head(&oi->i_wq); | |
932 | + set_obj_created(oi); | |
933 | + | |
934 | + /* copy stuff from on-disk struct to in-memory struct */ | |
935 | + inode->i_mode = le16_to_cpu(fcb.i_mode); | |
936 | + inode->i_uid = le32_to_cpu(fcb.i_uid); | |
937 | + inode->i_gid = le32_to_cpu(fcb.i_gid); | |
938 | + inode->i_nlink = le16_to_cpu(fcb.i_links_count); | |
939 | + inode->i_ctime.tv_sec = (signed)le32_to_cpu(fcb.i_ctime); | |
940 | + inode->i_atime.tv_sec = (signed)le32_to_cpu(fcb.i_atime); | |
941 | + inode->i_mtime.tv_sec = (signed)le32_to_cpu(fcb.i_mtime); | |
942 | + inode->i_ctime.tv_nsec = | |
943 | + inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = 0; | |
944 | + oi->i_commit_size = le64_to_cpu(fcb.i_size); | |
945 | + i_size_write(inode, oi->i_commit_size); | |
946 | + inode->i_blkbits = EXOFS_BLKSHIFT; | |
947 | + inode->i_generation = le32_to_cpu(fcb.i_generation); | |
948 | + | |
949 | +#ifdef EXOFS_DEBUG_OBJ_ISIZE | |
950 | + if ((inode->i_size != sanity) && | |
951 | + (!exofs_inode_is_fast_symlink(inode))) { | |
952 | + EXOFS_ERR("WARNING: Size of object from inode and " | |
953 | + "attributes differ (%lld != %llu)\n", | |
954 | + inode->i_size, _LLU(sanity)); | |
955 | + } | |
956 | +#endif | |
957 | + | |
958 | + oi->i_dir_start_lookup = 0; | |
959 | + | |
960 | + if ((inode->i_nlink == 0) && (inode->i_mode == 0)) { | |
961 | + ret = -ESTALE; | |
962 | + goto bad_inode; | |
963 | + } | |
964 | + | |
965 | + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { | |
966 | + if (fcb.i_data[0]) | |
967 | + inode->i_rdev = | |
968 | + old_decode_dev(le32_to_cpu(fcb.i_data[0])); | |
969 | + else | |
970 | + inode->i_rdev = | |
971 | + new_decode_dev(le32_to_cpu(fcb.i_data[1])); | |
972 | + } else { | |
973 | + memcpy(oi->i_data, fcb.i_data, sizeof(fcb.i_data)); | |
974 | + } | |
975 | + | |
976 | + if (S_ISREG(inode->i_mode)) { | |
977 | + inode->i_op = &exofs_file_inode_operations; | |
978 | + inode->i_fop = &exofs_file_operations; | |
979 | + inode->i_mapping->a_ops = &exofs_aops; | |
980 | + } else if (S_ISDIR(inode->i_mode)) { | |
981 | + inode->i_op = &exofs_dir_inode_operations; | |
982 | + inode->i_fop = &exofs_dir_operations; | |
983 | + inode->i_mapping->a_ops = &exofs_aops; | |
984 | + } else if (S_ISLNK(inode->i_mode)) { | |
985 | + if (exofs_inode_is_fast_symlink(inode)) | |
986 | + inode->i_op = &exofs_fast_symlink_inode_operations; | |
987 | + else { | |
988 | + inode->i_op = &exofs_symlink_inode_operations; | |
989 | + inode->i_mapping->a_ops = &exofs_aops; | |
990 | + } | |
991 | + } else { | |
992 | + inode->i_op = &exofs_special_inode_operations; | |
993 | + if (fcb.i_data[0]) | |
994 | + init_special_inode(inode, inode->i_mode, | |
995 | + old_decode_dev(le32_to_cpu(fcb.i_data[0]))); | |
996 | + else | |
997 | + init_special_inode(inode, inode->i_mode, | |
998 | + new_decode_dev(le32_to_cpu(fcb.i_data[1]))); | |
999 | + } | |
1000 | + | |
1001 | + unlock_new_inode(inode); | |
1002 | + return inode; | |
1003 | + | |
1004 | +bad_inode: | |
1005 | + iget_failed(inode); | |
1006 | + return ERR_PTR(ret); | |
1007 | +} | |
1008 | + | |
1009 | +int __exofs_wait_obj_created(struct exofs_i_info *oi) | |
1010 | +{ | |
1011 | + if (!obj_created(oi)) { | |
1012 | + BUG_ON(!obj_2bcreated(oi)); | |
1013 | + wait_event(oi->i_wq, obj_created(oi)); | |
1014 | + } | |
1015 | + return unlikely(is_bad_inode(&oi->vfs_inode)) ? -EIO : 0; | |
1016 | +} | |
1017 | +/* | |
1018 | + * Callback function from exofs_new_inode(). The important thing is that we | |
1019 | + * set the obj_created flag so that other methods know that the object exists on | |
1020 | + * the OSD. | |
1021 | + */ | |
1022 | +static void create_done(struct osd_request *or, void *p) | |
1023 | +{ | |
1024 | + struct inode *inode = p; | |
1025 | + struct exofs_i_info *oi = exofs_i(inode); | |
1026 | + struct exofs_sb_info *sbi = inode->i_sb->s_fs_info; | |
1027 | + int ret; | |
1028 | + | |
1029 | + ret = exofs_check_ok(or); | |
1030 | + osd_end_request(or); | |
1031 | + atomic_dec(&sbi->s_curr_pending); | |
1032 | + | |
1033 | + if (unlikely(ret)) { | |
1034 | + EXOFS_ERR("object=0x%llx creation faild in pid=0x%llx", | |
1035 | + _LLU(sbi->s_pid), _LLU(inode->i_ino + EXOFS_OBJ_OFF)); | |
1036 | + make_bad_inode(inode); | |
1037 | + } else | |
1038 | + set_obj_created(oi); | |
1039 | + | |
1040 | + atomic_dec(&inode->i_count); | |
1041 | + wake_up(&oi->i_wq); | |
1042 | +} | |
1043 | + | |
1044 | +/* | |
1045 | + * Set up a new inode and create an object for it on the OSD | |
1046 | + */ | |
1047 | +struct inode *exofs_new_inode(struct inode *dir, int mode) | |
1048 | +{ | |
1049 | + struct super_block *sb; | |
1050 | + struct inode *inode; | |
1051 | + struct exofs_i_info *oi; | |
1052 | + struct exofs_sb_info *sbi; | |
1053 | + struct osd_request *or; | |
1054 | + struct osd_obj_id obj; | |
1055 | + int ret; | |
1056 | + | |
1057 | + sb = dir->i_sb; | |
1058 | + inode = new_inode(sb); | |
1059 | + if (!inode) | |
1060 | + return ERR_PTR(-ENOMEM); | |
1061 | + | |
1062 | + oi = exofs_i(inode); | |
1063 | + | |
1064 | + init_waitqueue_head(&oi->i_wq); | |
1065 | + set_obj_2bcreated(oi); | |
1066 | + | |
1067 | + sbi = sb->s_fs_info; | |
1068 | + | |
1069 | + sb->s_dirt = 1; | |
1070 | + inode->i_uid = current->cred->fsuid; | |
1071 | + if (dir->i_mode & S_ISGID) { | |
1072 | + inode->i_gid = dir->i_gid; | |
1073 | + if (S_ISDIR(mode)) | |
1074 | + mode |= S_ISGID; | |
1075 | + } else { | |
1076 | + inode->i_gid = current->cred->fsgid; | |
1077 | + } | |
1078 | + inode->i_mode = mode; | |
1079 | + | |
1080 | + inode->i_ino = sbi->s_nextid++; | |
1081 | + inode->i_blkbits = EXOFS_BLKSHIFT; | |
1082 | + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | |
1083 | + oi->i_commit_size = inode->i_size = 0; | |
1084 | + spin_lock(&sbi->s_next_gen_lock); | |
1085 | + inode->i_generation = sbi->s_next_generation++; | |
1086 | + spin_unlock(&sbi->s_next_gen_lock); | |
1087 | + insert_inode_hash(inode); | |
1088 | + | |
1089 | + mark_inode_dirty(inode); | |
1090 | + | |
1091 | + obj.partition = sbi->s_pid; | |
1092 | + obj.id = inode->i_ino + EXOFS_OBJ_OFF; | |
1093 | + exofs_make_credential(oi->i_cred, &obj); | |
1094 | + | |
1095 | + or = osd_start_request(sbi->s_dev, GFP_KERNEL); | |
1096 | + if (unlikely(!or)) { | |
1097 | + EXOFS_ERR("exofs_new_inode: osd_start_request failed\n"); | |
1098 | + return ERR_PTR(-ENOMEM); | |
1099 | + } | |
1100 | + | |
1101 | + osd_req_create_object(or, &obj); | |
1102 | + | |
1103 | + /* increment the refcount so that the inode will still be around when we | |
1104 | + * reach the callback | |
1105 | + */ | |
1106 | + atomic_inc(&inode->i_count); | |
1107 | + | |
1108 | + ret = exofs_async_op(or, create_done, inode, oi->i_cred); | |
1109 | + if (ret) { | |
1110 | + atomic_dec(&inode->i_count); | |
1111 | + osd_end_request(or); | |
1112 | + return ERR_PTR(-EIO); | |
1113 | + } | |
1114 | + atomic_inc(&sbi->s_curr_pending); | |
1115 | + | |
1116 | + return inode; | |
1117 | +} |
fs/exofs/namei.c
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 | +}; |