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 (S_ISDIR(inode
->i_mode
)) {
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
;
244 if (S_ISDIR(inode
->i_mode
) &&
245 !omfs_dir_is_empty(inode
))
248 ret
= omfs_delete_entry(dentry
);
253 mark_inode_dirty(inode
);
254 mark_inode_dirty(dir
);
258 static int omfs_add_node(struct inode
*dir
, struct dentry
*dentry
, int mode
)
261 struct inode
*inode
= omfs_new_inode(dir
, mode
);
264 return PTR_ERR(inode
);
266 err
= omfs_make_empty(inode
, dir
->i_sb
);
270 err
= omfs_add_link(dentry
, inode
);
274 d_instantiate(dentry
, inode
);
282 static int omfs_mkdir(struct inode
*dir
, struct dentry
*dentry
, int mode
)
284 return omfs_add_node(dir
, dentry
, mode
| S_IFDIR
);
287 static int omfs_create(struct inode
*dir
, struct dentry
*dentry
, int mode
,
288 struct nameidata
*nd
)
290 return omfs_add_node(dir
, dentry
, mode
| S_IFREG
);
293 static struct dentry
*omfs_lookup(struct inode
*dir
, struct dentry
*dentry
,
294 struct nameidata
*nd
)
296 struct buffer_head
*bh
;
297 struct inode
*inode
= NULL
;
299 if (dentry
->d_name
.len
> OMFS_NAMELEN
)
300 return ERR_PTR(-ENAMETOOLONG
);
302 bh
= omfs_find_entry(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
);
304 struct omfs_inode
*oi
= (struct omfs_inode
*)bh
->b_data
;
305 ino_t ino
= be64_to_cpu(oi
->i_head
.h_self
);
307 inode
= omfs_iget(dir
->i_sb
, ino
);
309 return ERR_CAST(inode
);
311 d_add(dentry
, inode
);
315 /* sanity check block's self pointer */
316 int omfs_is_bad(struct omfs_sb_info
*sbi
, struct omfs_header
*header
,
320 u64 ino
= be64_to_cpu(header
->h_self
);
321 is_bad
= ((ino
!= fsblock
) || (ino
< sbi
->s_root_ino
) ||
322 (ino
> sbi
->s_num_blocks
));
325 printk(KERN_WARNING
"omfs: bad hash chain detected\n");
330 static int omfs_fill_chain(struct file
*filp
, void *dirent
, filldir_t filldir
,
331 u64 fsblock
, int hindex
)
333 struct inode
*dir
= filp
->f_dentry
->d_inode
;
334 struct buffer_head
*bh
;
335 struct omfs_inode
*oi
;
338 unsigned char d_type
;
340 /* follow chain in this bucket */
341 while (fsblock
!= ~0) {
342 bh
= omfs_bread(dir
->i_sb
, fsblock
);
346 oi
= (struct omfs_inode
*) bh
->b_data
;
347 if (omfs_is_bad(OMFS_SB(dir
->i_sb
), &oi
->i_head
, fsblock
)) {
353 fsblock
= be64_to_cpu(oi
->i_sibling
);
355 /* skip visited nodes */
362 d_type
= (oi
->i_type
== OMFS_DIR
) ? DT_DIR
: DT_REG
;
364 res
= filldir(dirent
, oi
->i_name
, strnlen(oi
->i_name
,
365 OMFS_NAMELEN
), filp
->f_pos
, self
, d_type
);
375 static int omfs_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
376 struct inode
*new_dir
, struct dentry
*new_dentry
)
378 struct inode
*new_inode
= new_dentry
->d_inode
;
379 struct inode
*old_inode
= old_dentry
->d_inode
;
383 /* overwriting existing file/dir */
384 err
= omfs_remove(new_dir
, new_dentry
);
389 /* since omfs locates files by name, we need to unlink _before_
390 * adding the new link or we won't find the old one */
391 err
= omfs_delete_entry(old_dentry
);
395 mark_inode_dirty(old_dir
);
396 err
= omfs_add_link(new_dentry
, old_inode
);
400 old_inode
->i_ctime
= CURRENT_TIME_SEC
;
401 mark_inode_dirty(old_inode
);
406 static int omfs_readdir(struct file
*filp
, void *dirent
, filldir_t filldir
)
408 struct inode
*dir
= filp
->f_dentry
->d_inode
;
409 struct buffer_head
*bh
;
411 unsigned int hchain
, hindex
;
416 if (filp
->f_pos
>> 32)
419 switch ((unsigned long) filp
->f_pos
) {
421 if (filldir(dirent
, ".", 1, 0, dir
->i_ino
, DT_DIR
) < 0)
426 if (filldir(dirent
, "..", 2, 1,
427 parent_ino(filp
->f_dentry
), DT_DIR
) < 0)
429 filp
->f_pos
= 1 << 20;
433 nbuckets
= (dir
->i_size
- OMFS_DIR_START
) / 8;
435 /* high 12 bits store bucket + 1 and low 20 bits store hash index */
436 hchain
= (filp
->f_pos
>> 20) - 1;
437 hindex
= filp
->f_pos
& 0xfffff;
439 bh
= omfs_bread(dir
->i_sb
, dir
->i_ino
);
443 offset
= OMFS_DIR_START
+ hchain
* 8;
445 for (; hchain
< nbuckets
; hchain
++, offset
+= 8) {
446 fsblock
= be64_to_cpu(*((__be64
*) &bh
->b_data
[offset
]));
448 res
= omfs_fill_chain(filp
, dirent
, filldir
, fsblock
, hindex
);
453 filp
->f_pos
= (hchain
+2) << 20;
462 const struct inode_operations omfs_dir_inops
= {
463 .lookup
= omfs_lookup
,
465 .rename
= omfs_rename
,
466 .create
= omfs_create
,
467 .unlink
= omfs_remove
,
468 .rmdir
= omfs_remove
,
471 const struct file_operations omfs_dir_operations
= {
472 .read
= generic_read_dir
,
473 .readdir
= omfs_readdir
,
474 .llseek
= generic_file_llseek
,