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
= d_inode(dentry
->d_parent
);
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(dir
);
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
= d_inode(dentry
->d_parent
);
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
= d_inode(dentry
);
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
, umode_t 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
, umode_t mode
)
284 return omfs_add_node(dir
, dentry
, mode
| S_IFDIR
);
287 static int omfs_create(struct inode
*dir
, struct dentry
*dentry
, umode_t mode
,
290 return omfs_add_node(dir
, dentry
, mode
| S_IFREG
);
293 static struct dentry
*omfs_lookup(struct inode
*dir
, struct dentry
*dentry
,
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
);
308 } else if (bh
!= ERR_PTR(-ENOENT
)) {
309 inode
= ERR_CAST(bh
);
311 return d_splice_alias(inode
, dentry
);
314 /* sanity check block's self pointer */
315 int omfs_is_bad(struct omfs_sb_info
*sbi
, struct omfs_header
*header
,
319 u64 ino
= be64_to_cpu(header
->h_self
);
320 is_bad
= ((ino
!= fsblock
) || (ino
< sbi
->s_root_ino
) ||
321 (ino
> sbi
->s_num_blocks
));
324 printk(KERN_WARNING
"omfs: bad hash chain detected\n");
329 static bool omfs_fill_chain(struct inode
*dir
, struct dir_context
*ctx
,
330 u64 fsblock
, int hindex
)
332 /* follow chain in this bucket */
333 while (fsblock
!= ~0) {
334 struct buffer_head
*bh
= omfs_bread(dir
->i_sb
, fsblock
);
335 struct omfs_inode
*oi
;
337 unsigned char d_type
;
342 oi
= (struct omfs_inode
*) bh
->b_data
;
343 if (omfs_is_bad(OMFS_SB(dir
->i_sb
), &oi
->i_head
, fsblock
)) {
349 fsblock
= be64_to_cpu(oi
->i_sibling
);
351 /* skip visited nodes */
358 d_type
= (oi
->i_type
== OMFS_DIR
) ? DT_DIR
: DT_REG
;
360 if (!dir_emit(ctx
, oi
->i_name
,
361 strnlen(oi
->i_name
, OMFS_NAMELEN
),
372 static int omfs_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
373 struct inode
*new_dir
, struct dentry
*new_dentry
,
376 struct inode
*new_inode
= d_inode(new_dentry
);
377 struct inode
*old_inode
= d_inode(old_dentry
);
380 if (flags
& ~RENAME_NOREPLACE
)
384 /* overwriting existing file/dir */
385 err
= omfs_remove(new_dir
, new_dentry
);
390 /* since omfs locates files by name, we need to unlink _before_
391 * adding the new link or we won't find the old one */
392 err
= omfs_delete_entry(old_dentry
);
396 mark_inode_dirty(old_dir
);
397 err
= omfs_add_link(new_dentry
, old_inode
);
401 old_inode
->i_ctime
= current_time(old_inode
);
402 mark_inode_dirty(old_inode
);
407 static int omfs_readdir(struct file
*file
, struct dir_context
*ctx
)
409 struct inode
*dir
= file_inode(file
);
410 struct buffer_head
*bh
;
412 unsigned int hchain
, hindex
;
418 if (ctx
->pos
< 1 << 20) {
419 if (!dir_emit_dots(file
, ctx
))
424 nbuckets
= (dir
->i_size
- OMFS_DIR_START
) / 8;
426 /* high 12 bits store bucket + 1 and low 20 bits store hash index */
427 hchain
= (ctx
->pos
>> 20) - 1;
428 hindex
= ctx
->pos
& 0xfffff;
430 bh
= omfs_bread(dir
->i_sb
, dir
->i_ino
);
434 p
= (__be64
*)(bh
->b_data
+ OMFS_DIR_START
) + hchain
;
436 for (; hchain
< nbuckets
; hchain
++) {
437 __u64 fsblock
= be64_to_cpu(*p
++);
438 if (!omfs_fill_chain(dir
, ctx
, fsblock
, hindex
))
441 ctx
->pos
= (hchain
+2) << 20;
447 const struct inode_operations omfs_dir_inops
= {
448 .lookup
= omfs_lookup
,
450 .rename
= omfs_rename
,
451 .create
= omfs_create
,
452 .unlink
= omfs_remove
,
453 .rmdir
= omfs_remove
,
456 const struct file_operations omfs_dir_operations
= {
457 .read
= generic_read_dir
,
458 .iterate_shared
= omfs_readdir
,
459 .llseek
= generic_file_llseek
,