Remove register keyword usage.
[dragonfly.git] / lib / libstand / ufs.c
1 /* $FreeBSD: src/lib/libstand/ufs.c,v 1.5.6.1 2000/05/04 13:47:53 ps Exp $ */
2 /* $DragonFly: src/lib/libstand/ufs.c,v 1.4 2004/10/25 19:38:45 drhodus Exp $ */
3 /*      $NetBSD: ufs.c,v 1.20 1998/03/01 07:15:39 ross Exp $    */
4
5 /*-
6  * Copyright (c) 1993
7  *      The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * The Mach Operating System project at Carnegie-Mellon University.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *  
40  *
41  * Copyright (c) 1990, 1991 Carnegie Mellon University
42  * All Rights Reserved.
43  *
44  * Author: David Golub
45  * 
46  * Permission to use, copy, modify and distribute this software and its
47  * documentation is hereby granted, provided that both the copyright
48  * notice and this permission notice appear in all copies of the
49  * software, derivative works or modified versions, and any portions
50  * thereof, and that both notices appear in supporting documentation.
51  * 
52  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
53  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
54  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
55  * 
56  * Carnegie Mellon requests users of this software to return to
57  * 
58  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
59  *  School of Computer Science
60  *  Carnegie Mellon University
61  *  Pittsburgh PA 15213-3890
62  * 
63  * any improvements or extensions that they make and grant Carnegie the
64  * rights to redistribute these changes.
65  */
66
67 /*
68  *      Stand-alone file reading package.
69  */
70
71 #include <sys/param.h>
72 #include <sys/time.h>
73 #include <vfs/ufs/dinode.h>
74 #include <vfs/ufs/dir.h>
75 #include <vfs/ufs/fs.h>
76 #include "stand.h"
77 #include "string.h"
78
79 #ifdef __alpha__
80 #define COMPAT_UFS              /* DUX has old format file systems */
81 #endif
82
83 static int      ufs_open(const char *path, struct open_file *f);
84 static int      ufs_close(struct open_file *f);
85 static int      ufs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
86 static off_t    ufs_seek(struct open_file *f, off_t offset, int where);
87 static int      ufs_stat(struct open_file *f, struct stat *sb);
88 static int      ufs_readdir(struct open_file *f, struct dirent *d);
89
90 struct fs_ops ufs_fsops = {
91         "ufs",
92         ufs_open,
93         ufs_close,
94         ufs_read,
95         null_write,
96         ufs_seek,
97         ufs_stat,
98         ufs_readdir
99 };
100
101 /*
102  * In-core open file.
103  */
104 struct file {
105         off_t           f_seekp;        /* seek pointer */
106         struct fs       *f_fs;          /* pointer to super-block */
107         struct dinode   f_di;           /* copy of on-disk inode */
108         int             f_nindir[NIADDR];
109                                         /* number of blocks mapped by
110                                            indirect block at level i */
111         char            *f_blk[NIADDR]; /* buffer for indirect block at
112                                            level i */
113         size_t          f_blksize[NIADDR];
114                                         /* size of buffer */
115         daddr_t         f_blkno[NIADDR];/* disk address of block in buffer */
116         char            *f_buf;         /* buffer for data block */
117         size_t          f_buf_size;     /* size of data block */
118         daddr_t         f_buf_blkno;    /* block number of data block */
119 };
120
121 static int      read_inode(ino_t, struct open_file *);
122 static int      block_map(struct open_file *, daddr_t, daddr_t *);
123 static int      buf_read_file(struct open_file *, char **, size_t *);
124 static int      search_directory(char *, struct open_file *, ino_t *);
125 #ifdef COMPAT_UFS
126 static void     ffs_oldfscompat(struct fs *);
127 #endif
128
129 /*
130  * Read a new inode into a file structure.
131  */
132 static int
133 read_inode(inumber, f)
134         ino_t inumber;
135         struct open_file *f;
136 {
137         struct file *fp = (struct file *)f->f_fsdata;
138         struct fs *fs = fp->f_fs;
139         char *buf;
140         size_t rsize;
141         int rc;
142
143         if (fs == NULL)
144             panic("fs == NULL");
145
146         /*
147          * Read inode and save it.
148          */
149         buf = malloc(fs->fs_bsize);
150         twiddle();
151         rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
152                 fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize,
153                 buf, &rsize);
154         if (rc)
155                 goto out;
156         if (rsize != fs->fs_bsize) {
157                 rc = EIO;
158                 goto out;
159         }
160
161         {
162                 struct dinode *dp;
163
164                 dp = (struct dinode *)buf;
165                 fp->f_di = dp[ino_to_fsbo(fs, inumber)];
166         }
167
168         /*
169          * Clear out the old buffers
170          */
171         {
172                 int level;
173
174                 for (level = 0; level < NIADDR; level++)
175                         fp->f_blkno[level] = -1;
176                 fp->f_buf_blkno = -1;
177         }
178 out:
179         free(buf);
180         return (rc);     
181 }
182
183 /*
184  * Given an offset in a file, find the disk block number that
185  * contains that block.
186  */
187 static int
188 block_map(f, file_block, disk_block_p)
189         struct open_file *f;
190         daddr_t file_block;
191         daddr_t *disk_block_p;  /* out */
192 {
193         struct file *fp = (struct file *)f->f_fsdata;
194         struct fs *fs = fp->f_fs;
195         int level;
196         int idx;
197         daddr_t ind_block_num;
198         daddr_t *ind_p;
199         int rc;
200
201         /*
202          * Index structure of an inode:
203          *
204          * di_db[0..NDADDR-1]   hold block numbers for blocks
205          *                      0..NDADDR-1
206          *
207          * di_ib[0]             index block 0 is the single indirect block
208          *                      holds block numbers for blocks
209          *                      NDADDR .. NDADDR + NINDIR(fs)-1
210          *
211          * di_ib[1]             index block 1 is the double indirect block
212          *                      holds block numbers for INDEX blocks for blocks
213          *                      NDADDR + NINDIR(fs) ..
214          *                      NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
215          *
216          * di_ib[2]             index block 2 is the triple indirect block
217          *                      holds block numbers for double-indirect
218          *                      blocks for blocks
219          *                      NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
220          *                      NDADDR + NINDIR(fs) + NINDIR(fs)**2
221          *                              + NINDIR(fs)**3 - 1
222          */
223
224         if (file_block < NDADDR) {
225                 /* Direct block. */
226                 *disk_block_p = fp->f_di.di_db[file_block];
227                 return (0);
228         }
229
230         file_block -= NDADDR;
231
232         /*
233          * nindir[0] = NINDIR
234          * nindir[1] = NINDIR**2
235          * nindir[2] = NINDIR**3
236          *      etc
237          */
238         for (level = 0; level < NIADDR; level++) {
239                 if (file_block < fp->f_nindir[level])
240                         break;
241                 file_block -= fp->f_nindir[level];
242         }
243         if (level == NIADDR) {
244                 /* Block number too high */
245                 return (EFBIG);
246         }
247
248         ind_block_num = fp->f_di.di_ib[level];
249
250         for (; level >= 0; level--) {
251                 if (ind_block_num == 0) {
252                         *disk_block_p = 0;      /* missing */
253                         return (0);
254                 }
255
256                 if (fp->f_blkno[level] != ind_block_num) {
257                         if (fp->f_blk[level] == (char *)0)
258                                 fp->f_blk[level] =
259                                         malloc(fs->fs_bsize);
260                         twiddle();
261                         rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
262                                 fsbtodb(fp->f_fs, ind_block_num),
263                                 fs->fs_bsize,
264                                 fp->f_blk[level],
265                                 &fp->f_blksize[level]);
266                         if (rc)
267                                 return (rc);
268                         if (fp->f_blksize[level] != fs->fs_bsize)
269                                 return (EIO);
270                         fp->f_blkno[level] = ind_block_num;
271                 }
272
273                 ind_p = (daddr_t *)fp->f_blk[level];
274
275                 if (level > 0) {
276                         idx = file_block / fp->f_nindir[level - 1];
277                         file_block %= fp->f_nindir[level - 1];
278                 } else
279                         idx = file_block;
280
281                 ind_block_num = ind_p[idx];
282         }
283
284         *disk_block_p = ind_block_num;
285
286         return (0);
287 }
288
289 /*
290  * Read a portion of a file into an internal buffer.  Return
291  * the location in the buffer and the amount in the buffer.
292  */
293 static int
294 buf_read_file(f, buf_p, size_p)
295         struct open_file *f;
296         char **buf_p;           /* out */
297         size_t *size_p;         /* out */
298 {
299         struct file *fp = (struct file *)f->f_fsdata;
300         struct fs *fs = fp->f_fs;
301         long off;
302         daddr_t file_block;
303         daddr_t disk_block;
304         size_t block_size;
305         int rc;
306
307         off = blkoff(fs, fp->f_seekp);
308         file_block = lblkno(fs, fp->f_seekp);
309         block_size = dblksize(fs, &fp->f_di, file_block);
310
311         if (file_block != fp->f_buf_blkno) {
312                 rc = block_map(f, file_block, &disk_block);
313                 if (rc)
314                         return (rc);
315
316                 if (fp->f_buf == (char *)0)
317                         fp->f_buf = malloc(fs->fs_bsize);
318
319                 if (disk_block == 0) {
320                         bzero(fp->f_buf, block_size);
321                         fp->f_buf_size = block_size;
322                 } else {
323                         twiddle();
324                         rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
325                                 fsbtodb(fs, disk_block),
326                                 block_size, fp->f_buf, &fp->f_buf_size);
327                         if (rc)
328                                 return (rc);
329                 }
330
331                 fp->f_buf_blkno = file_block;
332         }
333
334         /*
335          * Return address of byte in buffer corresponding to
336          * offset, and size of remainder of buffer after that
337          * byte.
338          */
339         *buf_p = fp->f_buf + off;
340         *size_p = block_size - off;
341
342         /*
343          * But truncate buffer at end of file.
344          */
345         if (*size_p > fp->f_di.di_size - fp->f_seekp)
346                 *size_p = fp->f_di.di_size - fp->f_seekp;
347
348         return (0);
349 }
350
351 /*
352  * Search a directory for a name and return its
353  * i_number.
354  */
355 static int
356 search_directory(name, f, inumber_p)
357         char *name;
358         struct open_file *f;
359         ino_t *inumber_p;               /* out */
360 {
361         struct file *fp = (struct file *)f->f_fsdata;
362         struct direct *dp;
363         struct direct *edp;
364         char *buf;
365         size_t buf_size;
366         int namlen, length;
367         int rc;
368
369         length = strlen(name);
370
371         fp->f_seekp = 0;
372         while (fp->f_seekp < fp->f_di.di_size) {
373                 rc = buf_read_file(f, &buf, &buf_size);
374                 if (rc)
375                         return (rc);
376
377                 dp = (struct direct *)buf;
378                 edp = (struct direct *)(buf + buf_size);
379                 while (dp < edp) {
380                         if (dp->d_ino == (ino_t)0)
381                                 goto next;
382 #if BYTE_ORDER == LITTLE_ENDIAN
383                         if (fp->f_fs->fs_maxsymlinklen <= 0)
384                                 namlen = dp->d_type;
385                         else
386 #endif
387                                 namlen = dp->d_namlen;
388                         if (namlen == length &&
389                             !strcmp(name, dp->d_name)) {
390                                 /* found entry */
391                                 *inumber_p = dp->d_ino;
392                                 return (0);
393                         }
394                 next:
395                         dp = (struct direct *)((char *)dp + dp->d_reclen);
396                 }
397                 fp->f_seekp += buf_size;
398         }
399         return (ENOENT);
400 }
401
402 /*
403  * Open a file.
404  */
405 static int
406 ufs_open(upath, f)
407         const char *upath;
408         struct open_file *f;
409 {
410         char *cp, *ncp;
411         int c;
412         ino_t inumber, parent_inumber;
413         struct file *fp;
414         struct fs *fs;
415         int rc;
416         size_t buf_size;
417         int nlinks = 0;
418         char namebuf[MAXPATHLEN+1];
419         char *buf = NULL;
420         char *path = NULL;
421
422         /* allocate file system specific data structure */
423         fp = malloc(sizeof(struct file));
424         bzero(fp, sizeof(struct file));
425         f->f_fsdata = (void *)fp;
426
427         /* allocate space and read super block */
428         fs = malloc(SBSIZE);
429         fp->f_fs = fs;
430         twiddle();
431         rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
432                 SBLOCK, SBSIZE, (char *)fs, &buf_size);
433         if (rc)
434                 goto out;
435
436         if (buf_size != SBSIZE || fs->fs_magic != FS_MAGIC ||
437             fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) {
438                 rc = EINVAL;
439                 goto out;
440         }
441 #ifdef COMPAT_UFS
442         ffs_oldfscompat(fs);
443 #endif
444
445         /*
446          * Calculate indirect block levels.
447          */
448         {
449                 int mult;
450                 int level;
451
452                 mult = 1;
453                 for (level = 0; level < NIADDR; level++) {
454                         mult *= NINDIR(fs);
455                         fp->f_nindir[level] = mult;
456                 }
457         }
458
459         inumber = ROOTINO;
460         if ((rc = read_inode(inumber, f)) != 0)
461                 goto out;
462
463         cp = path = strdup(upath);
464         if (path == NULL) {
465             rc = ENOMEM;
466             goto out;
467         }
468         while (*cp) {
469
470                 /*
471                  * Remove extra separators
472                  */
473                 while (*cp == '/')
474                         cp++;
475                 if (*cp == '\0')
476                         break;
477
478                 /*
479                  * Check that current node is a directory.
480                  */
481                 if ((fp->f_di.di_mode & IFMT) != IFDIR) {
482                         rc = ENOTDIR;
483                         goto out;
484                 }
485
486                 /*
487                  * Get next component of path name.
488                  */
489                 {
490                         int len = 0;
491
492                         ncp = cp;
493                         while ((c = *cp) != '\0' && c != '/') {
494                                 if (++len > MAXNAMLEN) {
495                                         rc = ENOENT;
496                                         goto out;
497                                 }
498                                 cp++;
499                         }
500                         *cp = '\0';
501                 }
502
503                 /*
504                  * Look up component in current directory.
505                  * Save directory inumber in case we find a
506                  * symbolic link.
507                  */
508                 parent_inumber = inumber;
509                 rc = search_directory(ncp, f, &inumber);
510                 *cp = c;
511                 if (rc)
512                         goto out;
513
514                 /*
515                  * Open next component.
516                  */
517                 if ((rc = read_inode(inumber, f)) != 0)
518                         goto out;
519
520                 /*
521                  * Check for symbolic link.
522                  */
523                 if ((fp->f_di.di_mode & IFMT) == IFLNK) {
524                         int link_len = fp->f_di.di_size;
525                         int len;
526
527                         len = strlen(cp);
528
529                         if (link_len + len > MAXPATHLEN ||
530                             ++nlinks > MAXSYMLINKS) {
531                                 rc = ENOENT;
532                                 goto out;
533                         }
534
535                         bcopy(cp, &namebuf[link_len], len + 1);
536
537                         if (link_len < fs->fs_maxsymlinklen) {
538                                 bcopy(fp->f_di.di_shortlink, namebuf,
539                                       (unsigned) link_len);
540                         } else {
541                                 /*
542                                  * Read file for symbolic link
543                                  */
544                                 size_t buf_size;
545                                 daddr_t disk_block;
546                                 struct fs *fs = fp->f_fs;
547
548                                 if (!buf)
549                                         buf = malloc(fs->fs_bsize);
550                                 rc = block_map(f, (daddr_t)0, &disk_block);
551                                 if (rc)
552                                         goto out;
553                                 
554                                 twiddle();
555                                 rc = (f->f_dev->dv_strategy)(f->f_devdata,
556                                         F_READ, fsbtodb(fs, disk_block),
557                                         fs->fs_bsize, buf, &buf_size);
558                                 if (rc)
559                                         goto out;
560
561                                 bcopy((char *)buf, namebuf, (unsigned)link_len);
562                         }
563
564                         /*
565                          * If relative pathname, restart at parent directory.
566                          * If absolute pathname, restart at root.
567                          */
568                         cp = namebuf;
569                         if (*cp != '/')
570                                 inumber = parent_inumber;
571                         else
572                                 inumber = (ino_t)ROOTINO;
573
574                         if ((rc = read_inode(inumber, f)) != 0)
575                                 goto out;
576                 }
577         }
578
579         /*
580          * Found terminal component.
581          */
582         rc = 0;
583 out:
584         if (buf)
585                 free(buf);
586         if (path)
587                 free(path);
588         if (rc) {
589                 if (fp->f_buf)
590                         free(fp->f_buf);
591                 free(fp->f_fs);
592                 free(fp);
593         }
594         return (rc);
595 }
596
597 static int
598 ufs_close(f)
599         struct open_file *f;
600 {
601         struct file *fp = (struct file *)f->f_fsdata;
602         int level;
603
604         f->f_fsdata = (void *)0;
605         if (fp == (struct file *)0)
606                 return (0);
607
608         for (level = 0; level < NIADDR; level++) {
609                 if (fp->f_blk[level])
610                         free(fp->f_blk[level]);
611         }
612         if (fp->f_buf)
613                 free(fp->f_buf);
614         free(fp->f_fs);
615         free(fp);
616         return (0);
617 }
618
619 /*
620  * Copy a portion of a file into kernel memory.
621  * Cross block boundaries when necessary.
622  */
623 static int
624 ufs_read(f, start, size, resid)
625         struct open_file *f;
626         void *start;
627         size_t size;
628         size_t *resid;  /* out */
629 {
630         struct file *fp = (struct file *)f->f_fsdata;
631         size_t csize;
632         char *buf;
633         size_t buf_size;
634         int rc = 0;
635         char *addr = start;
636
637         while (size != 0) {
638                 if (fp->f_seekp >= fp->f_di.di_size)
639                         break;
640
641                 rc = buf_read_file(f, &buf, &buf_size);
642                 if (rc)
643                         break;
644
645                 csize = size;
646                 if (csize > buf_size)
647                         csize = buf_size;
648
649                 bcopy(buf, addr, csize);
650
651                 fp->f_seekp += csize;
652                 addr += csize;
653                 size -= csize;
654         }
655         if (resid)
656                 *resid = size;
657         return (rc);
658 }
659
660 static off_t
661 ufs_seek(f, offset, where)
662         struct open_file *f;
663         off_t offset;
664         int where;
665 {
666         struct file *fp = (struct file *)f->f_fsdata;
667
668         switch (where) {
669         case SEEK_SET:
670                 fp->f_seekp = offset;
671                 break;
672         case SEEK_CUR:
673                 fp->f_seekp += offset;
674                 break;
675         case SEEK_END:
676                 fp->f_seekp = fp->f_di.di_size - offset;
677                 break;
678         default:
679                 return (-1);
680         }
681         return (fp->f_seekp);
682 }
683
684 static int
685 ufs_stat(f, sb)
686         struct open_file *f;
687         struct stat *sb;
688 {
689         struct file *fp = (struct file *)f->f_fsdata;
690
691         /* only important stuff */
692         sb->st_mode = fp->f_di.di_mode;
693         sb->st_uid = fp->f_di.di_uid;
694         sb->st_gid = fp->f_di.di_gid;
695         sb->st_size = fp->f_di.di_size;
696         return (0);
697 }
698
699 static int
700 ufs_readdir(struct open_file *f, struct dirent *d)
701 {
702         struct file *fp = (struct file *)f->f_fsdata;
703         struct direct *dp;
704         char *buf;
705         size_t buf_size;
706         int error;
707
708         /*
709          * assume that a directory entry will not be split across blocks
710          */
711 again:
712         if (fp->f_seekp >= fp->f_di.di_size)
713                 return (ENOENT);
714         error = buf_read_file(f, &buf, &buf_size);
715         if (error)
716                 return (error);
717         dp = (struct direct *)buf;
718         fp->f_seekp += dp->d_reclen;
719         if (dp->d_ino == (ino_t)0)
720                 goto again;
721         d->d_type = dp->d_type;
722         strcpy(d->d_name, dp->d_name);
723         return (0);
724 }
725
726 #ifdef COMPAT_UFS
727 /*
728  * Sanity checks for old file systems.
729  *
730  * XXX - goes away some day.
731  */
732 static void
733 ffs_oldfscompat(fs)
734         struct fs *fs;
735 {
736         int i;
737
738         fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect);       /* XXX */
739         fs->fs_interleave = max(fs->fs_interleave, 1);          /* XXX */
740         if (fs->fs_postblformat == FS_42POSTBLFMT)              /* XXX */
741                 fs->fs_nrpos = 8;                               /* XXX */
742         if (fs->fs_inodefmt < FS_44INODEFMT) {                  /* XXX */
743                 quad_t sizepb = fs->fs_bsize;                   /* XXX */
744                                                                 /* XXX */
745                 fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */
746                 for (i = 0; i < NIADDR; i++) {                  /* XXX */
747                         sizepb *= NINDIR(fs);                   /* XXX */
748                         fs->fs_maxfilesize += sizepb;           /* XXX */
749                 }                                               /* XXX */
750                 fs->fs_qbmask = ~fs->fs_bmask;                  /* XXX */
751                 fs->fs_qfmask = ~fs->fs_fmask;                  /* XXX */
752         }                                                       /* XXX */
753 }
754 #endif