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