2 * OMFS (as used by RIO Karma) directory operations.
3 * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com>
4 * Released under GPL v2.
8 #include <linux/ctype.h>
9 #include <linux/buffer_head.h>
12 static int omfs_hash(const char *name
, int namelen
, int mod
)
15 for (i
= 0; i
< namelen
; i
++)
16 hash
^= tolower(name
[i
]) << (i
% 24);
21 * Finds the bucket for a given name and reads the containing block;
22 * *ofs is set to the offset of the first list entry.
24 static struct buffer_head
*omfs_get_bucket(struct inode
*dir
,
25 const char *name
, int namelen
, int *ofs
)
27 int nbuckets
= (dir
->i_size
- OMFS_DIR_START
)/8;
28 int bucket
= omfs_hash(name
, namelen
, nbuckets
);
30 *ofs
= OMFS_DIR_START
+ bucket
* 8;
31 return omfs_bread(dir
->i_sb
, dir
->i_ino
);
34 static struct buffer_head
*omfs_scan_list(struct inode
*dir
, u64 block
,
35 const char *name
, int namelen
,
38 struct buffer_head
*bh
;
39 struct omfs_inode
*oi
;
44 bh
= omfs_bread(dir
->i_sb
, block
);
50 oi
= (struct omfs_inode
*) bh
->b_data
;
51 if (omfs_is_bad(OMFS_SB(dir
->i_sb
), &oi
->i_head
, block
)) {
56 if (strncmp(oi
->i_name
, name
, namelen
) == 0)
60 block
= be64_to_cpu(oi
->i_sibling
);
67 static struct buffer_head
*omfs_find_entry(struct inode
*dir
,
68 const char *name
, int namelen
)
70 struct buffer_head
*bh
;
74 bh
= omfs_get_bucket(dir
, name
, namelen
, &ofs
);
78 block
= be64_to_cpu(*((__be64
*) &bh
->b_data
[ofs
]));
81 return omfs_scan_list(dir
, block
, name
, namelen
, &dummy
);
84 int omfs_make_empty(struct inode
*inode
, struct super_block
*sb
)
86 struct omfs_sb_info
*sbi
= OMFS_SB(sb
);
87 struct buffer_head
*bh
;
88 struct omfs_inode
*oi
;
90 bh
= omfs_bread(sb
, inode
->i_ino
);
94 memset(bh
->b_data
, 0, sizeof(struct omfs_inode
));
96 if (inode
->i_mode
& S_IFDIR
) {
97 memset(&bh
->b_data
[OMFS_DIR_START
], 0xff,
98 sbi
->s_sys_blocksize
- OMFS_DIR_START
);
100 omfs_make_empty_table(bh
, OMFS_EXTENT_START
);
102 oi
= (struct omfs_inode
*) bh
->b_data
;
103 oi
->i_head
.h_self
= cpu_to_be64(inode
->i_ino
);
104 oi
->i_sibling
= ~cpu_to_be64(0ULL);
106 mark_buffer_dirty(bh
);
111 static int omfs_add_link(struct dentry
*dentry
, struct inode
*inode
)
113 struct inode
*dir
= dentry
->d_parent
->d_inode
;
114 const char *name
= dentry
->d_name
.name
;
115 int namelen
= dentry
->d_name
.len
;
116 struct omfs_inode
*oi
;
117 struct buffer_head
*bh
;
122 /* just prepend to head of queue in proper bucket */
123 bh
= omfs_get_bucket(dir
, name
, namelen
, &ofs
);
127 entry
= (__be64
*) &bh
->b_data
[ofs
];
128 block
= be64_to_cpu(*entry
);
129 *entry
= cpu_to_be64(inode
->i_ino
);
130 mark_buffer_dirty(bh
);
133 /* now set the sibling and parent pointers on the new inode */
134 bh
= omfs_bread(dir
->i_sb
, inode
->i_ino
);
138 oi
= (struct omfs_inode
*) bh
->b_data
;
139 memcpy(oi
->i_name
, name
, namelen
);
140 memset(oi
->i_name
+ namelen
, 0, OMFS_NAMELEN
- namelen
);
141 oi
->i_sibling
= cpu_to_be64(block
);
142 oi
->i_parent
= cpu_to_be64(dir
->i_ino
);
143 mark_buffer_dirty(bh
);
146 dir
->i_ctime
= CURRENT_TIME_SEC
;
148 /* mark affected inodes dirty to rebuild checksums */
149 mark_inode_dirty(dir
);
150 mark_inode_dirty(inode
);
156 static int omfs_delete_entry(struct dentry
*dentry
)
158 struct inode
*dir
= dentry
->d_parent
->d_inode
;
160 const char *name
= dentry
->d_name
.name
;
161 int namelen
= dentry
->d_name
.len
;
162 struct omfs_inode
*oi
;
163 struct buffer_head
*bh
, *bh2
;
169 /* delete the proper node in the bucket's linked list */
170 bh
= omfs_get_bucket(dir
, name
, namelen
, &ofs
);
174 entry
= (__be64
*) &bh
->b_data
[ofs
];
175 block
= be64_to_cpu(*entry
);
177 bh2
= omfs_scan_list(dir
, block
, name
, namelen
, &prev
);
183 oi
= (struct omfs_inode
*) bh2
->b_data
;
184 next
= oi
->i_sibling
;
188 /* found in middle of list, get list ptr */
190 bh
= omfs_bread(dir
->i_sb
, prev
);
194 oi
= (struct omfs_inode
*) bh
->b_data
;
195 entry
= &oi
->i_sibling
;
199 mark_buffer_dirty(bh
);
202 dirty
= omfs_iget(dir
->i_sb
, prev
);
203 if (!IS_ERR(dirty
)) {
204 mark_inode_dirty(dirty
);
216 static int omfs_dir_is_empty(struct inode
*inode
)
218 int nbuckets
= (inode
->i_size
- OMFS_DIR_START
) / 8;
219 struct buffer_head
*bh
;
223 bh
= omfs_bread(inode
->i_sb
, inode
->i_ino
);
228 ptr
= (u64
*) &bh
->b_data
[OMFS_DIR_START
];
230 for (i
= 0; i
< nbuckets
; i
++, ptr
++)
238 static int omfs_remove(struct inode
*dir
, struct dentry
*dentry
)
240 struct inode
*inode
= dentry
->d_inode
;
243 if (S_ISDIR(inode
->i_mode
) && !omfs_dir_is_empty(inode
))
246 ret
= omfs_delete_entry(dentry
);
251 mark_inode_dirty(inode
);
252 mark_inode_dirty(dir
);
256 static int omfs_add_node(struct inode
*dir
, struct dentry
*dentry
, int mode
)
259 struct inode
*inode
= omfs_new_inode(dir
, mode
);
262 return PTR_ERR(inode
);
264 err
= omfs_make_empty(inode
, dir
->i_sb
);
268 err
= omfs_add_link(dentry
, inode
);
272 d_instantiate(dentry
, inode
);
280 static int omfs_mkdir(struct inode
*dir
, struct dentry
*dentry
, int mode
)
282 return omfs_add_node(dir
, dentry
, mode
| S_IFDIR
);
285 static int omfs_create(struct inode
*dir
, struct dentry
*dentry
, int mode
,
286 struct nameidata
*nd
)
288 return omfs_add_node(dir
, dentry
, mode
| S_IFREG
);
291 static struct dentry
*omfs_lookup(struct inode
*dir
, struct dentry
*dentry
,
292 struct nameidata
*nd
)
294 struct buffer_head
*bh
;
295 struct inode
*inode
= NULL
;
297 if (dentry
->d_name
.len
> OMFS_NAMELEN
)
298 return ERR_PTR(-ENAMETOOLONG
);
300 bh
= omfs_find_entry(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
);
302 struct omfs_inode
*oi
= (struct omfs_inode
*)bh
->b_data
;
303 ino_t ino
= be64_to_cpu(oi
->i_head
.h_self
);
305 inode
= omfs_iget(dir
->i_sb
, ino
);
307 return ERR_CAST(inode
);
309 d_add(dentry
, inode
);
313 /* sanity check block's self pointer */
314 int omfs_is_bad(struct omfs_sb_info
*sbi
, struct omfs_header
*header
,
318 u64 ino
= be64_to_cpu(header
->h_self
);
319 is_bad
= ((ino
!= fsblock
) || (ino
< sbi
->s_root_ino
) ||
320 (ino
> sbi
->s_num_blocks
));
323 printk(KERN_WARNING
"omfs: bad hash chain detected\n");
328 static int omfs_fill_chain(struct file
*filp
, void *dirent
, filldir_t filldir
,
329 u64 fsblock
, int hindex
)
331 struct inode
*dir
= filp
->f_dentry
->d_inode
;
332 struct buffer_head
*bh
;
333 struct omfs_inode
*oi
;
336 unsigned char d_type
;
338 /* follow chain in this bucket */
339 while (fsblock
!= ~0) {
340 bh
= omfs_bread(dir
->i_sb
, fsblock
);
344 oi
= (struct omfs_inode
*) bh
->b_data
;
345 if (omfs_is_bad(OMFS_SB(dir
->i_sb
), &oi
->i_head
, fsblock
)) {
351 fsblock
= be64_to_cpu(oi
->i_sibling
);
353 /* skip visited nodes */
360 d_type
= (oi
->i_type
== OMFS_DIR
) ? DT_DIR
: DT_REG
;
362 res
= filldir(dirent
, oi
->i_name
, strnlen(oi
->i_name
,
363 OMFS_NAMELEN
), filp
->f_pos
, self
, d_type
);
373 static int omfs_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
374 struct inode
*new_dir
, struct dentry
*new_dentry
)
376 struct inode
*new_inode
= new_dentry
->d_inode
;
377 struct inode
*old_inode
= old_dentry
->d_inode
;
381 /* overwriting existing file/dir */
382 err
= omfs_remove(new_dir
, new_dentry
);
387 /* since omfs locates files by name, we need to unlink _before_
388 * adding the new link or we won't find the old one */
389 err
= omfs_delete_entry(old_dentry
);
393 mark_inode_dirty(old_dir
);
394 err
= omfs_add_link(new_dentry
, old_inode
);
398 old_inode
->i_ctime
= CURRENT_TIME_SEC
;
399 mark_inode_dirty(old_inode
);
404 static int omfs_readdir(struct file
*filp
, void *dirent
, filldir_t filldir
)
406 struct inode
*dir
= filp
->f_dentry
->d_inode
;
407 struct buffer_head
*bh
;
409 unsigned int hchain
, hindex
;
414 if (filp
->f_pos
>> 32)
417 switch ((unsigned long) filp
->f_pos
) {
419 if (filldir(dirent
, ".", 1, 0, dir
->i_ino
, DT_DIR
) < 0)
424 if (filldir(dirent
, "..", 2, 1,
425 parent_ino(filp
->f_dentry
), DT_DIR
) < 0)
427 filp
->f_pos
= 1 << 20;
431 nbuckets
= (dir
->i_size
- OMFS_DIR_START
) / 8;
433 /* high 12 bits store bucket + 1 and low 20 bits store hash index */
434 hchain
= (filp
->f_pos
>> 20) - 1;
435 hindex
= filp
->f_pos
& 0xfffff;
437 bh
= omfs_bread(dir
->i_sb
, dir
->i_ino
);
441 offset
= OMFS_DIR_START
+ hchain
* 8;
443 for (; hchain
< nbuckets
; hchain
++, offset
+= 8) {
444 fsblock
= be64_to_cpu(*((__be64
*) &bh
->b_data
[offset
]));
446 res
= omfs_fill_chain(filp
, dirent
, filldir
, fsblock
, hindex
);
451 filp
->f_pos
= (hchain
+2) << 20;
460 const struct inode_operations omfs_dir_inops
= {
461 .lookup
= omfs_lookup
,
463 .rename
= omfs_rename
,
464 .create
= omfs_create
,
465 .unlink
= omfs_remove
,
466 .rmdir
= omfs_remove
,
469 const struct file_operations omfs_dir_operations
= {
470 .read
= generic_read_dir
,
471 .readdir
= omfs_readdir
,
472 .llseek
= generic_file_llseek
,