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