ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / ufs / dir.c
1 /*
2  *  linux/fs/ufs/ufs_dir.c
3  *
4  * Copyright (C) 1996
5  * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
6  * Laboratory for Computer Science Research Computing Facility
7  * Rutgers, The State University of New Jersey
8  *
9  * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406
10  *
11  * 4.4BSD (FreeBSD) support added on February 1st 1998 by
12  * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
13  * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
14  */
15
16 #include <linux/time.h>
17 #include <linux/fs.h>
18 #include <linux/ufs_fs.h>
19 #include <linux/smp_lock.h>
20 #include <linux/buffer_head.h>
21 #include <linux/sched.h>
22
23 #include "swab.h"
24 #include "util.h"
25
26 #undef UFS_DIR_DEBUG
27
28 #ifdef UFS_DIR_DEBUG
29 #define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
30 #else
31 #define UFSD(x)
32 #endif
33
34
35
36 /*
37  * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure.
38  *
39  * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller.
40  */
41 static inline int ufs_match(struct super_block *sb, int len,
42                 const char * const name, struct ufs_dir_entry * de)
43 {
44         if (len != ufs_get_de_namlen(sb, de))
45                 return 0;
46         if (!de->d_ino)
47                 return 0;
48         return !memcmp(name, de->d_name, len);
49 }
50
51 /*
52  * This is blatantly stolen from ext2fs
53  */
54 static int
55 ufs_readdir (struct file * filp, void * dirent, filldir_t filldir)
56 {
57         struct inode *inode = filp->f_dentry->d_inode;
58         int error = 0;
59         unsigned long offset, lblk;
60         int i, stored;
61         struct buffer_head * bh;
62         struct ufs_dir_entry * de;
63         struct super_block * sb;
64         int de_reclen;
65         unsigned flags;
66         u64     blk= 0L;
67
68         lock_kernel();
69
70         sb = inode->i_sb;
71         flags = UFS_SB(sb)->s_flags;
72
73         UFSD(("ENTER, ino %lu  f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos))
74
75         stored = 0;
76         bh = NULL;
77         offset = filp->f_pos & (sb->s_blocksize - 1);
78
79         while (!error && !stored && filp->f_pos < inode->i_size) {
80                 lblk = (filp->f_pos) >> sb->s_blocksize_bits;
81                 blk = ufs_frag_map(inode, lblk);
82                 if (!blk || !(bh = sb_bread(sb, blk))) {
83                         /* XXX - error - skip to the next block */
84                         printk("ufs_readdir: "
85                                "dir inode %lu has a hole at offset %lu\n",
86                                inode->i_ino, (unsigned long int)filp->f_pos);
87                         filp->f_pos += sb->s_blocksize - offset;
88                         continue;
89                 }
90
91 revalidate:
92                 /* If the dir block has changed since the last call to
93                  * readdir(2), then we might be pointing to an invalid
94                  * dirent right now.  Scan from the start of the block
95                  * to make sure. */
96                 if (filp->f_version != inode->i_version) {
97                         for (i = 0; i < sb->s_blocksize && i < offset; ) {
98                                 de = (struct ufs_dir_entry *)(bh->b_data + i);
99                                 /* It's too expensive to do a full
100                                  * dirent test each time round this
101                                  * loop, but we do have to test at
102                                  * least that it is non-zero.  A
103                                  * failure will be detected in the
104                                  * dirent test below. */
105                                 de_reclen = fs16_to_cpu(sb, de->d_reclen);
106                                 if (de_reclen < 1)
107                                         break;
108                                 i += de_reclen;
109                         }
110                         offset = i;
111                         filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
112                                 | offset;
113                         filp->f_version = inode->i_version;
114                 }
115
116                 while (!error && filp->f_pos < inode->i_size
117                        && offset < sb->s_blocksize) {
118                         de = (struct ufs_dir_entry *) (bh->b_data + offset);
119                         /* XXX - put in a real ufs_check_dir_entry() */
120                         if ((de->d_reclen == 0) || (ufs_get_de_namlen(sb, de) == 0)) {
121                                 filp->f_pos = (filp->f_pos &
122                                               (sb->s_blocksize - 1)) +
123                                                sb->s_blocksize;
124                                 brelse(bh);
125                                 unlock_kernel();
126                                 return stored;
127                         }
128                         if (!ufs_check_dir_entry ("ufs_readdir", inode, de,
129                                                    bh, offset)) {
130                                 /* On error, skip the f_pos to the
131                                    next block. */
132                                 filp->f_pos = (filp->f_pos |
133                                               (sb->s_blocksize - 1)) +
134                                                1;
135                                 brelse (bh);
136                                 unlock_kernel();
137                                 return stored;
138                         }
139                         offset += fs16_to_cpu(sb, de->d_reclen);
140                         if (de->d_ino) {
141                                 /* We might block in the next section
142                                  * if the data destination is
143                                  * currently swapped out.  So, use a
144                                  * version stamp to detect whether or
145                                  * not the directory has been modified
146                                  * during the copy operation. */
147                                 unsigned long version = filp->f_version;
148                                 unsigned char d_type = DT_UNKNOWN;
149
150                                 UFSD(("filldir(%s,%u)\n", de->d_name,
151                                                         fs32_to_cpu(sb, de->d_ino)))
152                                 UFSD(("namlen %u\n", ufs_get_de_namlen(sb, de)))
153
154                                 if ((flags & UFS_DE_MASK) == UFS_DE_44BSD)
155                                         d_type = de->d_u.d_44.d_type;
156                                 error = filldir(dirent, de->d_name,
157                                                 ufs_get_de_namlen(sb, de), filp->f_pos,
158                                                 fs32_to_cpu(sb, de->d_ino), d_type);
159                                 if (error)
160                                         break;
161                                 if (version != filp->f_version)
162                                         goto revalidate;
163                                 stored ++;
164                         }
165                         filp->f_pos += fs16_to_cpu(sb, de->d_reclen);
166                 }
167                 offset = 0;
168                 brelse (bh);
169         }
170         unlock_kernel();
171         return 0;
172 }
173
174 /*
175  * define how far ahead to read directories while searching them.
176  */
177 #define NAMEI_RA_CHUNKS  2
178 #define NAMEI_RA_BLOCKS  4
179 #define NAMEI_RA_SIZE        (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
180 #define NAMEI_RA_INDEX(c,b)  (((c) * NAMEI_RA_BLOCKS) + (b))
181
182 /*
183  *      ufs_find_entry()
184  *
185  * finds an entry in the specified directory with the wanted name. It
186  * returns the cache buffer in which the entry was found, and the entry
187  * itself (as a parameter - res_bh). It does NOT read the inode of the
188  * entry - you'll have to do that yourself if you want to.
189  */
190 struct ufs_dir_entry * ufs_find_entry (struct dentry *dentry,
191         struct buffer_head ** res_bh)
192 {
193         struct super_block * sb;
194         struct buffer_head * bh_use[NAMEI_RA_SIZE];
195         struct buffer_head * bh_read[NAMEI_RA_SIZE];
196         unsigned long offset;
197         int block, toread, i, err;
198         struct inode *dir = dentry->d_parent->d_inode;
199         const char *name = dentry->d_name.name;
200         int namelen = dentry->d_name.len;
201
202         UFSD(("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen))
203         
204         *res_bh = NULL;
205         
206         sb = dir->i_sb;
207         
208         if (namelen > UFS_MAXNAMLEN)
209                 return NULL;
210
211         memset (bh_use, 0, sizeof (bh_use));
212         toread = 0;
213         for (block = 0; block < NAMEI_RA_SIZE; ++block) {
214                 struct buffer_head * bh;
215
216                 if ((block << sb->s_blocksize_bits) >= dir->i_size)
217                         break;
218                 bh = ufs_getfrag (dir, block, 0, &err);
219                 bh_use[block] = bh;
220                 if (bh && !buffer_uptodate(bh))
221                         bh_read[toread++] = bh;
222         }
223
224         for (block = 0, offset = 0; offset < dir->i_size; block++) {
225                 struct buffer_head * bh;
226                 struct ufs_dir_entry * de;
227                 char * dlimit;
228
229                 if ((block % NAMEI_RA_BLOCKS) == 0 && toread) {
230                         ll_rw_block (READ, toread, bh_read);
231                         toread = 0;
232                 }
233                 bh = bh_use[block % NAMEI_RA_SIZE];
234                 if (!bh) {
235                         ufs_error (sb, "ufs_find_entry", 
236                                 "directory #%lu contains a hole at offset %lu",
237                                 dir->i_ino, offset);
238                         offset += sb->s_blocksize;
239                         continue;
240                 }
241                 wait_on_buffer (bh);
242                 if (!buffer_uptodate(bh)) {
243                         /*
244                          * read error: all bets are off
245                          */
246                         break;
247                 }
248
249                 de = (struct ufs_dir_entry *) bh->b_data;
250                 dlimit = bh->b_data + sb->s_blocksize;
251                 while ((char *) de < dlimit && offset < dir->i_size) {
252                         /* this code is executed quadratically often */
253                         /* do minimal checking by hand */
254                         int de_len;
255
256                         if ((char *) de + namelen <= dlimit &&
257                             ufs_match(sb, namelen, name, de)) {
258                                 /* found a match -
259                                 just to be sure, do a full check */
260                                 if (!ufs_check_dir_entry("ufs_find_entry",
261                                     dir, de, bh, offset))
262                                         goto failed;
263                                 for (i = 0; i < NAMEI_RA_SIZE; ++i) {
264                                         if (bh_use[i] != bh)
265                                                 brelse (bh_use[i]);
266                                 }
267                                 *res_bh = bh;
268                                 return de;
269                         }
270                         /* prevent looping on a bad block */
271                         de_len = fs16_to_cpu(sb, de->d_reclen);
272                         if (de_len <= 0)
273                                 goto failed;
274                         offset += de_len;
275                         de = (struct ufs_dir_entry *) ((char *) de + de_len);
276                 }
277
278                 brelse (bh);
279                 if (((block + NAMEI_RA_SIZE) << sb->s_blocksize_bits ) >=
280                     dir->i_size)
281                         bh = NULL;
282                 else
283                         bh = ufs_getfrag (dir, block + NAMEI_RA_SIZE, 0, &err);
284                 bh_use[block % NAMEI_RA_SIZE] = bh;
285                 if (bh && !buffer_uptodate(bh))
286                         bh_read[toread++] = bh;
287         }
288
289 failed:
290         for (i = 0; i < NAMEI_RA_SIZE; ++i) brelse (bh_use[i]);
291         UFSD(("EXIT\n"))
292         return NULL;
293 }
294
295 int ufs_check_dir_entry (const char * function, struct inode * dir,
296         struct ufs_dir_entry * de, struct buffer_head * bh, 
297         unsigned long offset)
298 {
299         struct super_block *sb = dir->i_sb;
300         const char *error_msg = NULL;
301         int rlen = fs16_to_cpu(sb, de->d_reclen);
302
303         if (rlen < UFS_DIR_REC_LEN(1))
304                 error_msg = "reclen is smaller than minimal";
305         else if (rlen % 4 != 0)
306                 error_msg = "reclen % 4 != 0";
307         else if (rlen < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)))
308                 error_msg = "reclen is too small for namlen";
309         else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize)
310                 error_msg = "directory entry across blocks";
311         else if (fs32_to_cpu(sb, de->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg *
312                                       UFS_SB(sb)->s_uspi->s_ncg))
313                 error_msg = "inode out of bounds";
314
315         if (error_msg != NULL)
316                 ufs_error (sb, function, "bad entry in directory #%lu, size %Lu: %s - "
317                             "offset=%lu, inode=%lu, reclen=%d, namlen=%d",
318                             dir->i_ino, dir->i_size, error_msg, offset,
319                             (unsigned long)fs32_to_cpu(sb, de->d_ino),
320                             rlen, ufs_get_de_namlen(sb, de));
321         
322         return (error_msg == NULL ? 1 : 0);
323 }
324
325 struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct buffer_head **p)
326 {
327         int err;
328         struct buffer_head *bh = ufs_bread (dir, 0, 0, &err);
329         struct ufs_dir_entry *res = NULL;
330
331         if (bh) {
332                 res = (struct ufs_dir_entry *) bh->b_data;
333                 res = (struct ufs_dir_entry *)((char *)res +
334                         fs16_to_cpu(dir->i_sb, res->d_reclen));
335         }
336         *p = bh;
337         return res;
338 }
339 ino_t ufs_inode_by_name(struct inode * dir, struct dentry *dentry)
340 {
341         ino_t res = 0;
342         struct ufs_dir_entry * de;
343         struct buffer_head *bh;
344
345         de = ufs_find_entry (dentry, &bh);
346         if (de) {
347                 res = fs32_to_cpu(dir->i_sb, de->d_ino);
348                 brelse(bh);
349         }
350         return res;
351 }
352
353 void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
354                 struct buffer_head *bh, struct inode *inode)
355 {
356         dir->i_version++;
357         de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino);
358         mark_buffer_dirty(bh);
359         if (IS_DIRSYNC(dir))
360                 sync_dirty_buffer(bh);
361         brelse (bh);
362 }
363
364 /*
365  *      ufs_add_entry()
366  *
367  * adds a file entry to the specified directory, using the same
368  * semantics as ufs_find_entry(). It returns NULL if it failed.
369  */
370 int ufs_add_link(struct dentry *dentry, struct inode *inode)
371 {
372         struct super_block * sb;
373         struct ufs_sb_private_info * uspi;
374         unsigned long offset;
375         unsigned fragoff;
376         unsigned short rec_len;
377         struct buffer_head * bh;
378         struct ufs_dir_entry * de, * de1;
379         struct inode *dir = dentry->d_parent->d_inode;
380         const char *name = dentry->d_name.name;
381         int namelen = dentry->d_name.len;
382         int err;
383
384         UFSD(("ENTER, name %s, namelen %u\n", name, namelen))
385         
386         sb = dir->i_sb;
387         uspi = UFS_SB(sb)->s_uspi;
388
389         if (!namelen)
390                 return -EINVAL;
391         bh = ufs_bread (dir, 0, 0, &err);
392         if (!bh)
393                 return err;
394         rec_len = UFS_DIR_REC_LEN(namelen);
395         offset = 0;
396         de = (struct ufs_dir_entry *) bh->b_data;
397         while (1) {
398                 if ((char *)de >= UFS_SECTOR_SIZE + bh->b_data) {
399                         fragoff = offset & ~uspi->s_fmask;
400                         if (fragoff != 0 && fragoff != UFS_SECTOR_SIZE)
401                                 ufs_error (sb, "ufs_add_entry", "internal error"
402                                         " fragoff %u", fragoff);
403                         if (!fragoff) {
404                                 brelse (bh);
405                                 bh = ufs_bread (dir, offset >> sb->s_blocksize_bits, 1, &err);
406                                 if (!bh)
407                                         return err;
408                         }
409                         if (dir->i_size <= offset) {
410                                 if (dir->i_size == 0) {
411                                         brelse(bh);
412                                         return -ENOENT;
413                                 }
414                                 de = (struct ufs_dir_entry *) (bh->b_data + fragoff);
415                                 de->d_ino = 0;
416                                 de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE);
417                                 ufs_set_de_namlen(sb, de, 0);
418                                 dir->i_size = offset + UFS_SECTOR_SIZE;
419                                 mark_inode_dirty(dir);
420                         } else {
421                                 de = (struct ufs_dir_entry *) bh->b_data;
422                         }
423                 }
424                 if (!ufs_check_dir_entry ("ufs_add_entry", dir, de, bh, offset)) {
425                         brelse (bh);
426                         return -ENOENT;
427                 }
428                 if (ufs_match(sb, namelen, name, de)) {
429                         brelse (bh);
430                         return -EEXIST;
431                 }
432                 if (de->d_ino == 0 && fs16_to_cpu(sb, de->d_reclen) >= rec_len)
433                         break;
434                         
435                 if (fs16_to_cpu(sb, de->d_reclen) >=
436                      UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)) + rec_len)
437                         break;
438                 offset += fs16_to_cpu(sb, de->d_reclen);
439                 de = (struct ufs_dir_entry *) ((char *) de + fs16_to_cpu(sb, de->d_reclen));
440         }
441
442         if (de->d_ino) {
443                 de1 = (struct ufs_dir_entry *) ((char *) de +
444                         UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
445                 de1->d_reclen =
446                         cpu_to_fs16(sb, fs16_to_cpu(sb, de->d_reclen) -
447                                 UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
448                 de->d_reclen =
449                         cpu_to_fs16(sb, UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
450                 de = de1;
451         }
452         de->d_ino = 0;
453         ufs_set_de_namlen(sb, de, namelen);
454         memcpy (de->d_name, name, namelen + 1);
455         de->d_ino = cpu_to_fs32(sb, inode->i_ino);
456         ufs_set_de_type(sb, de, inode->i_mode);
457         mark_buffer_dirty(bh);
458         if (IS_DIRSYNC(dir))
459                 sync_dirty_buffer(bh);
460         brelse (bh);
461         dir->i_mtime = dir->i_ctime = CURRENT_TIME;
462         dir->i_version++;
463         mark_inode_dirty(dir);
464
465         UFSD(("EXIT\n"))
466         return 0;
467 }
468
469 /*
470  * ufs_delete_entry deletes a directory entry by merging it with the
471  * previous entry.
472  */
473 int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir,
474         struct buffer_head * bh )
475         
476 {
477         struct super_block * sb;
478         struct ufs_dir_entry * de, * pde;
479         unsigned i;
480         
481         UFSD(("ENTER\n"))
482
483         sb = inode->i_sb;
484         i = 0;
485         pde = NULL;
486         de = (struct ufs_dir_entry *) bh->b_data;
487         
488         UFSD(("ino %u, reclen %u, namlen %u, name %s\n",
489                 fs32_to_cpu(sb, de->d_ino),
490                 fs16to_cpu(sb, de->d_reclen),
491                 ufs_get_de_namlen(sb, de), de->d_name))
492
493         while (i < bh->b_size) {
494                 if (!ufs_check_dir_entry ("ufs_delete_entry", inode, de, bh, i)) {
495                         brelse(bh);
496                         return -EIO;
497                 }
498                 if (de == dir)  {
499                         if (pde)
500                                 fs16_add(sb, &pde->d_reclen,
501                                         fs16_to_cpu(sb, dir->d_reclen));
502                         dir->d_ino = 0;
503                         inode->i_version++;
504                         inode->i_ctime = inode->i_mtime = CURRENT_TIME;
505                         mark_inode_dirty(inode);
506                         mark_buffer_dirty(bh);
507                         if (IS_DIRSYNC(inode))
508                                 sync_dirty_buffer(bh);
509                         brelse(bh);
510                         UFSD(("EXIT\n"))
511                         return 0;
512                 }
513                 i += fs16_to_cpu(sb, de->d_reclen);
514                 if (i == UFS_SECTOR_SIZE) pde = NULL;
515                 else pde = de;
516                 de = (struct ufs_dir_entry *)
517                     ((char *) de + fs16_to_cpu(sb, de->d_reclen));
518                 if (i == UFS_SECTOR_SIZE && de->d_reclen == 0)
519                         break;
520         }
521         UFSD(("EXIT\n"))
522         brelse(bh);
523         return -ENOENT;
524 }
525
526 int ufs_make_empty(struct inode * inode, struct inode *dir)
527 {
528         struct super_block * sb = dir->i_sb;
529         struct buffer_head * dir_block;
530         struct ufs_dir_entry * de;
531         int err;
532
533         dir_block = ufs_bread (inode, 0, 1, &err);
534         if (!dir_block)
535                 return err;
536
537         inode->i_blocks = sb->s_blocksize / UFS_SECTOR_SIZE;
538         de = (struct ufs_dir_entry *) dir_block->b_data;
539         de->d_ino = cpu_to_fs32(sb, inode->i_ino);
540         ufs_set_de_type(sb, de, inode->i_mode);
541         ufs_set_de_namlen(sb, de, 1);
542         de->d_reclen = cpu_to_fs16(sb, UFS_DIR_REC_LEN(1));
543         strcpy (de->d_name, ".");
544         de = (struct ufs_dir_entry *)
545                 ((char *)de + fs16_to_cpu(sb, de->d_reclen));
546         de->d_ino = cpu_to_fs32(sb, dir->i_ino);
547         ufs_set_de_type(sb, de, dir->i_mode);
548         de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1));
549         ufs_set_de_namlen(sb, de, 2);
550         strcpy (de->d_name, "..");
551         mark_buffer_dirty(dir_block);
552         brelse (dir_block);
553         mark_inode_dirty(inode);
554         return 0;
555 }
556
557 /*
558  * routine to check that the specified directory is empty (for rmdir)
559  */
560 int ufs_empty_dir (struct inode * inode)
561 {
562         struct super_block * sb;
563         unsigned long offset;
564         struct buffer_head * bh;
565         struct ufs_dir_entry * de, * de1;
566         int err;
567         
568         sb = inode->i_sb;
569
570         if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) ||
571             !(bh = ufs_bread (inode, 0, 0, &err))) {
572                 ufs_warning (inode->i_sb, "empty_dir",
573                               "bad directory (dir #%lu) - no data block",
574                               inode->i_ino);
575                 return 1;
576         }
577         de = (struct ufs_dir_entry *) bh->b_data;
578         de1 = (struct ufs_dir_entry *)
579                 ((char *)de + fs16_to_cpu(sb, de->d_reclen));
580         if (fs32_to_cpu(sb, de->d_ino) != inode->i_ino || de1->d_ino == 0 ||
581              strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) {
582                 ufs_warning (inode->i_sb, "empty_dir",
583                               "bad directory (dir #%lu) - no `.' or `..'",
584                               inode->i_ino);
585                 return 1;
586         }
587         offset = fs16_to_cpu(sb, de->d_reclen) + fs16_to_cpu(sb, de1->d_reclen);
588         de = (struct ufs_dir_entry *)
589                 ((char *)de1 + fs16_to_cpu(sb, de1->d_reclen));
590         while (offset < inode->i_size ) {
591                 if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
592                         brelse (bh);
593                         bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err);
594                         if (!bh) {
595                                 ufs_error (sb, "empty_dir",
596                                             "directory #%lu contains a hole at offset %lu",
597                                             inode->i_ino, offset);
598                                 offset += sb->s_blocksize;
599                                 continue;
600                         }
601                         de = (struct ufs_dir_entry *) bh->b_data;
602                 }
603                 if (!ufs_check_dir_entry ("empty_dir", inode, de, bh, offset)) {
604                         brelse (bh);
605                         return 1;
606                 }
607                 if (de->d_ino) {
608                         brelse (bh);
609                         return 0;
610                 }
611                 offset += fs16_to_cpu(sb, de->d_reclen);
612                 de = (struct ufs_dir_entry *)
613                         ((char *)de + fs16_to_cpu(sb, de->d_reclen));
614         }
615         brelse (bh);
616         return 1;
617 }
618
619 struct file_operations ufs_dir_operations = {
620         .read           = generic_read_dir,
621         .readdir        = ufs_readdir,
622         .fsync          = file_fsync,
623 };