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