- pre3:
[davej-history.git] / fs / bfs / dir.c
blob721739e31a8680a054662a101731c700457618a3
1 /*
2 * fs/bfs/dir.c
3 * BFS directory operations.
4 * Copyright (C) 1999 Tigran Aivazian <tigran@ocston.org>
5 */
7 #include <linux/sched.h>
8 #include <linux/string.h>
9 #include <linux/bfs_fs.h>
10 #include <linux/locks.h>
12 #include "bfs_defs.h"
14 #undef DEBUG
16 #ifdef DEBUG
17 #define dprintf(x...) printf(x)
18 #else
19 #define dprintf(x...)
20 #endif
22 static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino);
23 static struct buffer_head * bfs_find_entry(struct inode * dir,
24 const char * name, int namelen, struct bfs_dirent ** res_dir);
26 static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir)
28 struct inode * dir = f->f_dentry->d_inode;
29 struct buffer_head * bh;
30 struct bfs_dirent * de;
31 kdev_t dev = dir->i_dev;
32 unsigned int offset;
33 int block;
35 if (f->f_pos & (BFS_DIRENT_SIZE-1)) {
36 printf("Bad f_pos=%08lx for %s:%08lx\n", (unsigned long)f->f_pos,
37 bdevname(dev), dir->i_ino);
38 return -EBADF;
41 while (f->f_pos < dir->i_size) {
42 offset = f->f_pos & (BFS_BSIZE-1);
43 block = dir->iu_sblock + (f->f_pos >> BFS_BSIZE_BITS);
44 bh = bread(dev, block, BFS_BSIZE);
45 if (!bh) {
46 f->f_pos += BFS_BSIZE - offset;
47 continue;
49 do {
50 de = (struct bfs_dirent *)(bh->b_data + offset);
51 if (de->ino) {
52 int size = strnlen(de->name, BFS_NAMELEN);
53 if (filldir(dirent, de->name, size, f->f_pos, de->ino, DT_UNKNOWN) < 0) {
54 brelse(bh);
55 return 0;
58 offset += BFS_DIRENT_SIZE;
59 f->f_pos += BFS_DIRENT_SIZE;
60 } while (offset < BFS_BSIZE && f->f_pos < dir->i_size);
61 brelse(bh);
64 UPDATE_ATIME(dir);
65 return 0;
68 struct file_operations bfs_dir_operations = {
69 read: generic_read_dir,
70 readdir: bfs_readdir,
71 fsync: file_fsync,
74 extern void dump_imap(const char *, struct super_block *);
76 static int bfs_create(struct inode * dir, struct dentry * dentry, int mode)
78 int err;
79 struct inode * inode;
80 struct super_block * s = dir->i_sb;
81 unsigned long ino;
83 inode = get_empty_inode();
84 if (!inode)
85 return -ENOSPC;
86 inode->i_sb = s;
87 ino = find_first_zero_bit(s->su_imap, s->su_lasti);
88 if (ino > s->su_lasti) {
89 iput(inode);
90 return -ENOSPC;
92 set_bit(ino, s->su_imap);
93 s->su_freei--;
94 inode->i_dev = s->s_dev;
95 inode->i_uid = current->fsuid;
96 inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
97 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
98 inode->i_blocks = inode->i_blksize = 0;
99 inode->i_op = &bfs_file_inops;
100 inode->i_fop = &bfs_file_operations;
101 inode->i_mapping->a_ops = &bfs_aops;
102 inode->i_mode = mode;
103 inode->i_ino = inode->iu_dsk_ino = ino;
104 inode->iu_sblock = inode->iu_eblock = 0;
105 insert_inode_hash(inode);
106 mark_inode_dirty(inode);
107 dump_imap("create",s);
109 err = bfs_add_entry(dir, dentry->d_name.name, dentry->d_name.len, inode->i_ino);
110 if (err) {
111 inode->i_nlink--;
112 mark_inode_dirty(inode);
113 iput(inode);
114 return err;
116 d_instantiate(dentry, inode);
117 return 0;
120 static struct dentry * bfs_lookup(struct inode * dir, struct dentry * dentry)
122 struct inode * inode = NULL;
123 struct buffer_head * bh;
124 struct bfs_dirent * de;
126 if (dentry->d_name.len > BFS_NAMELEN)
127 return ERR_PTR(-ENAMETOOLONG);
129 bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
130 if (bh) {
131 unsigned long ino = le32_to_cpu(de->ino);
132 brelse(bh);
133 inode = iget(dir->i_sb, ino);
134 if (!inode)
135 return ERR_PTR(-EACCES);
137 d_add(dentry, inode);
138 return NULL;
141 static int bfs_link(struct dentry * old, struct inode * dir, struct dentry * new)
143 struct inode * inode = old->d_inode;
144 int err;
146 if (S_ISDIR(inode->i_mode))
147 return -EPERM;
149 err = bfs_add_entry(dir, new->d_name.name, new->d_name.len, inode->i_ino);
150 if (err)
151 return err;
152 inode->i_nlink++;
153 inode->i_ctime = CURRENT_TIME;
154 mark_inode_dirty(inode);
155 atomic_inc(&inode->i_count);
156 d_instantiate(new, inode);
157 return 0;
161 static int bfs_unlink(struct inode * dir, struct dentry * dentry)
163 int error = -ENOENT;
164 struct inode * inode;
165 struct buffer_head * bh;
166 struct bfs_dirent * de;
168 inode = dentry->d_inode;
169 bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
170 if (!bh || de->ino != inode->i_ino)
171 goto out_brelse;
173 if (!inode->i_nlink) {
174 printf("unlinking non-existent file %s:%lu (nlink=%d)\n", bdevname(inode->i_dev),
175 inode->i_ino, inode->i_nlink);
176 inode->i_nlink = 1;
178 de->ino = 0;
179 dir->i_version = ++event;
180 mark_buffer_dirty(bh, 0);
181 dir->i_ctime = dir->i_mtime = CURRENT_TIME;
182 mark_inode_dirty(dir);
183 inode->i_nlink--;
184 inode->i_ctime = dir->i_ctime;
185 mark_inode_dirty(inode);
186 error = 0;
188 out_brelse:
189 brelse(bh);
190 return error;
193 static int bfs_rename(struct inode * old_dir, struct dentry * old_dentry,
194 struct inode * new_dir, struct dentry * new_dentry)
196 struct inode * old_inode, * new_inode;
197 struct buffer_head * old_bh, * new_bh;
198 struct bfs_dirent * old_de, * new_de;
199 int error = -ENOENT;
201 old_bh = new_bh = NULL;
202 old_inode = old_dentry->d_inode;
203 if (S_ISDIR(old_inode->i_mode))
204 return -EINVAL;
206 old_bh = bfs_find_entry(old_dir,
207 old_dentry->d_name.name,
208 old_dentry->d_name.len, &old_de);
210 if (!old_bh || old_de->ino != old_inode->i_ino)
211 goto end_rename;
213 error = -EPERM;
214 new_inode = new_dentry->d_inode;
215 new_bh = bfs_find_entry(new_dir,
216 new_dentry->d_name.name,
217 new_dentry->d_name.len, &new_de);
219 if (new_bh && !new_inode) {
220 brelse(new_bh);
221 new_bh = NULL;
223 if (!new_bh) {
224 error = bfs_add_entry(new_dir,
225 new_dentry->d_name.name,
226 new_dentry->d_name.len, old_inode->i_ino);
227 if (error)
228 goto end_rename;
230 old_de->ino = 0;
231 old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
232 old_dir->i_version = ++event;
233 mark_inode_dirty(old_dir);
234 if (new_inode) {
235 new_inode->i_nlink--;
236 new_inode->i_ctime = CURRENT_TIME;
237 mark_inode_dirty(new_inode);
239 mark_buffer_dirty(old_bh, 0);
240 error = 0;
242 end_rename:
243 brelse(old_bh);
244 brelse(new_bh);
245 return error;
248 struct inode_operations bfs_dir_inops = {
249 create: bfs_create,
250 lookup: bfs_lookup,
251 link: bfs_link,
252 unlink: bfs_unlink,
253 rename: bfs_rename,
256 static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino)
258 struct buffer_head * bh;
259 struct bfs_dirent * de;
260 int block, sblock, eblock, off;
261 kdev_t dev;
262 int i;
264 dprintf("name=%s, namelen=%d\n", name, namelen);
266 if (!namelen)
267 return -ENOENT;
268 if (namelen > BFS_NAMELEN)
269 return -ENAMETOOLONG;
271 dev = dir->i_dev;
272 sblock = dir->iu_sblock;
273 eblock = dir->iu_eblock;
274 for (block=sblock; block<=eblock; block++) {
275 bh = bread(dev, block, BFS_BSIZE);
276 if(!bh)
277 return -ENOSPC;
278 for (off=0; off<BFS_BSIZE; off+=BFS_DIRENT_SIZE) {
279 de = (struct bfs_dirent *)(bh->b_data + off);
280 if (!de->ino) {
281 if ((block-sblock)*BFS_BSIZE + off >= dir->i_size) {
282 dir->i_size += BFS_DIRENT_SIZE;
283 dir->i_ctime = CURRENT_TIME;
285 dir->i_mtime = CURRENT_TIME;
286 mark_inode_dirty(dir);
287 dir->i_version = ++event;
288 de->ino = ino;
289 for (i=0; i<BFS_NAMELEN; i++)
290 de->name[i] = (i < namelen) ? name[i] : 0;
291 mark_buffer_dirty(bh, 0);
292 brelse(bh);
293 return 0;
296 brelse(bh);
298 return -ENOSPC;
301 static inline int bfs_namecmp(int len, const char * name, const char * buffer)
303 if (len < BFS_NAMELEN && buffer[len])
304 return 0;
305 return !memcmp(name, buffer, len);
308 static struct buffer_head * bfs_find_entry(struct inode * dir,
309 const char * name, int namelen, struct bfs_dirent ** res_dir)
311 unsigned long block, offset;
312 struct buffer_head * bh;
313 struct bfs_sb_info * info;
314 struct bfs_dirent * de;
316 *res_dir = NULL;
317 info = &dir->i_sb->u.bfs_sb;
318 if (namelen > BFS_NAMELEN)
319 return NULL;
320 bh = NULL;
321 block = offset = 0;
322 while (block * BFS_BSIZE + offset < dir->i_size) {
323 if (!bh) {
324 bh = bread(dir->i_dev, dir->iu_sblock + block, BFS_BSIZE);
325 if (!bh) {
326 block++;
327 continue;
330 de = (struct bfs_dirent *)(bh->b_data + offset);
331 offset += BFS_DIRENT_SIZE;
332 if (de->ino && bfs_namecmp(namelen, name, de->name)) {
333 *res_dir = de;
334 return bh;
336 if (offset < bh->b_size)
337 continue;
338 brelse(bh);
339 bh = NULL;
340 offset = 0;
341 block++;
343 brelse(bh);
344 return NULL;