1 /*-
2 * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 /*-
28 * Copyright (c) 1993
29 * The Regents of the University of California. All rights reserved.
30 *
31 * This code is derived from software contributed to Berkeley by
32 * The Mach Operating System project at Carnegie-Mellon University.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
36 * are met:
37 * 1. Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * 2. Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in the
41 * documentation and/or other materials provided with the distribution.
42 * 3. All advertising materials mentioning features or use of this software
43 * must display the following acknowledgement:
44 * This product includes software developed by the University of
45 * California, Berkeley and its contributors.
46 * 4. Neither the name of the University nor the names of its contributors
47 * may be used to endorse or promote products derived from this software
48 * without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * SUCH DAMAGE.
61 *
62 *
63 * Copyright (c) 1990, 1991 Carnegie Mellon University
64 * All Rights Reserved.
65 *
66 * Author: David Golub
67 *
68 * Permission to use, copy, modify and distribute this software and its
69 * documentation is hereby granted, provided that both the copyright
70 * notice and this permission notice appear in all copies of the
71 * software, derivative works or modified versions, and any portions
72 * thereof, and that both notices appear in supporting documentation.
73 *
74 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
75 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
76 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
77 *
78 * Carnegie Mellon requests users of this software to return to
79 *
80 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
81 * School of Computer Science
82 * Carnegie Mellon University
83 * Pittsburgh PA 15213-3890
84 *
85 * any improvements or extensions that they make and grant Carnegie the
86 * rights to redistribute these changes.
87 */
88
89 #include <sys/param.h>
90 #include <sys/time.h>
91 #include "stand.h"
92 #include "string.h"
93
94 static int ext2fs_open(const char *path, struct open_file *f);
95 static int ext2fs_close(struct open_file *f);
96 static int ext2fs_read(struct open_file *f, void *buf,
97 size_t size, size_t *resid);
98 static off_t ext2fs_seek(struct open_file *f, off_t offset, int where);
99 static int ext2fs_stat(struct open_file *f, struct stat *sb);
100 static int ext2fs_readdir(struct open_file *f, struct dirent *d);
101
102 static int dtmap[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR,
103 DT_BLK, DT_FIFO, DT_SOCK, DT_LNK };
104 #define EXTFTODT(x) (x) > sizeof(dtmap) / sizeof(dtmap[0]) ? \
105 DT_UNKNOWN : dtmap[x]
106
107 struct fs_ops ext2fs_fsops = {
108 .fs_name = "ext2fs",
109 .fs_flags = 0,
110 .fo_open = ext2fs_open,
111 .fo_close = ext2fs_close,
112 .fo_read = ext2fs_read,
113 .fo_write = null_write,
114 .fo_seek = ext2fs_seek,
115 .fo_stat = ext2fs_stat,
116 .fo_readdir = ext2fs_readdir,
117 };
118
119 #define EXT2_SBSIZE 1024
120 #define EXT2_SBLOCK (1024 / DEV_BSIZE) /* block offset of superblock */
121 #define EXT2_MAGIC 0xef53
122 #define EXT2_ROOTINO 2
123
124 #define EXT2_REV0 0 /* original revision of ext2 */
125 #define EXT2_R0_ISIZE 128 /* inode size */
126 #define EXT2_R0_FIRSTINO 11 /* first inode */
127
128 #define EXT2_MINBSHIFT 10 /* minimum block shift */
129 #define EXT2_MINFSHIFT 10 /* minimum frag shift */
130
131 #define EXT2_NDADDR 12 /* # of direct blocks */
132 #define EXT2_NIADDR 3 /* # of indirect blocks */
133
134 /*
135 * file system block to disk address
136 */
137 #define fsb_to_db(fs, blk) ((blk) << (fs)->fs_fsbtodb)
138
139 /*
140 * inode to block group offset
141 * inode to block group
142 * inode to disk address
143 * inode to block offset
144 */
145 #define ino_to_bgo(fs, ino) (((ino) - 1) % (fs)->fs_ipg)
146 #define ino_to_bg(fs, ino) (((ino) - 1) / (fs)->fs_ipg)
147 #define ino_to_db(fs, bg, ino) \
148 fsb_to_db(fs, ((bg)[ino_to_bg(fs, ino)].bg_inotbl + \
149 ino_to_bgo(fs, ino) / (fs)->fs_ipb))
150 #define ino_to_bo(fs, ino) (ino_to_bgo(fs, ino) % (fs)->fs_ipb)
151
152 #define nindir(fs) \
153 ((fs)->fs_bsize / sizeof(uint32_t))
154 #define lblkno(fs, loc) /* loc / bsize */ \
155 ((loc) >> (fs)->fs_bshift)
156 #define smalllblktosize(fs, blk) /* blk * bsize */ \
157 ((blk) << (fs)->fs_bshift)
158 #define blkoff(fs, loc) /* loc % bsize */ \
159 ((loc) & (fs)->fs_bmask)
160 #define fragroundup(fs, size) /* roundup(size, fsize) */ \
161 (((size) + (fs)->fs_fmask) & ~(fs)->fs_fmask)
162 #define dblksize(fs, dip, lbn) \
163 (((lbn) >= EXT2_NDADDR || (dip)->di_size >= smalllblktosize(fs, (lbn) + 1)) \
164 ? (fs)->fs_bsize \
165 : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
166
167 /*
168 * superblock describing ext2fs
169 */
170 struct ext2fs_disk {
171 uint32_t fd_inodes; /* # of inodes */
172 uint32_t fd_blocks; /* # of blocks */
173 uint32_t fd_resblk; /* # of reserved blocks */
174 uint32_t fd_freeblk; /* # of free blocks */
175 uint32_t fd_freeino; /* # of free inodes */
176 uint32_t fd_firstblk; /* first data block */
177 uint32_t fd_bsize; /* block size */
178 uint32_t fd_fsize; /* frag size */
179 uint32_t fd_bpg; /* blocks per group */
180 uint32_t fd_fpg; /* frags per group */
181 uint32_t fd_ipg; /* inodes per group */
182 uint32_t fd_mtime; /* mount time */
183 uint32_t fd_wtime; /* write time */
184 uint16_t fd_mount; /* # of mounts */
185 int16_t fd_maxmount; /* max # of mounts */
186 uint16_t fd_magic; /* magic number */
187 uint16_t fd_state; /* state */
188 uint16_t fd_eflag; /* error flags */
189 uint16_t fd_mnrrev; /* minor revision */
190 uint32_t fd_lastchk; /* last check */
191 uint32_t fd_chkintvl; /* maximum check interval */
192 uint32_t fd_os; /* os */
193 uint32_t fd_revision; /* revision */
194 uint16_t fd_uid; /* uid for reserved blocks */
195 uint16_t fd_gid; /* gid for reserved blocks */
196
197 uint32_t fd_firstino; /* first non-reserved inode */
198 uint16_t fd_isize; /* inode size */
199 uint16_t fd_nblkgrp; /* block group # of superblock */
200 uint32_t fd_fcompat; /* compatible features */
201 uint32_t fd_fincompat; /* incompatible features */
202 uint32_t fd_frocompat; /* read-only compatibilties */
203 uint8_t fd_uuid[16]; /* volume uuid */
204 char fd_volname[16]; /* volume name */
205 char fd_fsmnt[64]; /* name last mounted on */
206 uint32_t fd_bitmap; /* compression bitmap */
207
208 uint8_t fd_nblkpa; /* # of blocks to preallocate */
209 uint8_t fd_ndblkpa; /* # of dir blocks to preallocate */
210 };
211
212 struct ext2fs_core {
213 int fc_bsize; /* block size */
214 int fc_bshift; /* block shift amount */
215 int fc_bmask; /* block mask */
216 int fc_fsize; /* frag size */
217 int fc_fshift; /* frag shift amount */
218 int fc_fmask; /* frag mask */
219 int fc_isize; /* inode size */
220 int fc_imask; /* inode mask */
221 int fc_firstino; /* first non-reserved inode */
222 int fc_ipb; /* inodes per block */
223 int fc_fsbtodb; /* fsb to ds shift */
224 };
225
226 struct ext2fs {
227 struct ext2fs_disk fs_fd;
228 char fs_pad[EXT2_SBSIZE - sizeof(struct ext2fs_disk)];
229 struct ext2fs_core fs_fc;
230
231 #define fs_magic fs_fd.fd_magic
232 #define fs_revision fs_fd.fd_revision
233 #define fs_blocks fs_fd.fd_blocks
234 #define fs_firstblk fs_fd.fd_firstblk
235 #define fs_bpg fs_fd.fd_bpg
236 #define fs_ipg fs_fd.fd_ipg
237
238 #define fs_bsize fs_fc.fc_bsize
239 #define fs_bshift fs_fc.fc_bshift
240 #define fs_bmask fs_fc.fc_bmask
241 #define fs_fsize fs_fc.fc_fsize
242 #define fs_fshift fs_fc.fc_fshift
243 #define fs_fmask fs_fc.fc_fmask
244 #define fs_isize fs_fc.fc_isize
245 #define fs_imask fs_fc.fc_imask
246 #define fs_firstino fs_fc.fc_firstino
247 #define fs_ipb fs_fc.fc_ipb
248 #define fs_fsbtodb fs_fc.fc_fsbtodb
249 };
250
251 struct ext2blkgrp {
252 uint32_t bg_blkmap; /* block bitmap */
253 uint32_t bg_inomap; /* inode bitmap */
254 uint32_t bg_inotbl; /* inode table */
255 uint16_t bg_nfblk; /* # of free blocks */
256 uint16_t bg_nfino; /* # of free inodes */
257 uint16_t bg_ndirs; /* # of dirs */
258 char bg_pad[14];
259 };
260
261 struct ext2dinode {
262 uint16_t di_mode; /* mode */
263 uint16_t di_uid; /* uid */
264 uint32_t di_size; /* byte size */
265 uint32_t di_atime; /* access time */
266 uint32_t di_ctime; /* creation time */
267 uint32_t di_mtime; /* modification time */
268 uint32_t di_dtime; /* deletion time */
269 uint16_t di_gid; /* gid */
270 uint16_t di_nlink; /* link count */
271 uint32_t di_nblk; /* block count */
272 uint32_t di_flags; /* file flags */
273
274 uint32_t di_osdep1; /* os dependent stuff */
275
276 uint32_t di_db[EXT2_NDADDR]; /* direct blocks */
277 uint32_t di_ib[EXT2_NIADDR]; /* indirect blocks */
278 uint32_t di_version; /* version */
279 uint32_t di_facl; /* file acl */
280 uint32_t di_dacl; /* dir acl */
281 uint32_t di_faddr; /* fragment addr */
282
283 uint8_t di_frag; /* fragment number */
284 uint8_t di_fsize; /* fragment size */
285
286 char di_pad[10];
287
288 #define di_shortlink di_db
289 };
290
291 #define EXT2_MAXNAMLEN 255
292
293 struct ext2dirent {
294 uint32_t d_ino; /* inode */
295 uint16_t d_reclen; /* directory entry length */
296 uint8_t d_namlen; /* name length */
297 uint8_t d_type; /* file type */
298 char d_name[EXT2_MAXNAMLEN];
299 };
300
301 struct file {
302 off_t f_seekp; /* seek pointer */
303 struct ext2fs *f_fs; /* pointer to super-block */
304 struct ext2blkgrp *f_bg; /* pointer to blkgrp map */
305 struct ext2dinode f_di; /* copy of on-disk inode */
306 int f_nindir[EXT2_NIADDR]; /* number of blocks mapped by
307 indirect block at level i */
308 char *f_blk[EXT2_NIADDR]; /* buffer for indirect block
309 at level i */
310 size_t f_blksize[EXT2_NIADDR]; /* size of buffer */
311 daddr_t f_blkno[EXT2_NIADDR]; /* disk address of block in
312 buffer */
313 char *f_buf; /* buffer for data block */
314 size_t f_buf_size; /* size of data block */
315 daddr_t f_buf_blkno; /* block number of data block */
316 };
317
318 /* forward decls */
319 static int read_inode(ino_t inumber, struct open_file *f);
320 static int block_map(struct open_file *f, daddr_t file_block,
321 daddr_t *disk_block_p);
322 static int buf_read_file(struct open_file *f, char **buf_p,
323 size_t *size_p);
324 static int search_directory(char *name, struct open_file *f,
325 ino_t *inumber_p);
326
327 /*
328 * Open a file.
329 */
330 static int
ext2fs_open(const char * upath,struct open_file * f)331 ext2fs_open(const char *upath, struct open_file *f)
332 {
333 struct file *fp;
334 struct ext2fs *fs;
335 size_t buf_size;
336 ino_t inumber, parent_inumber;
337 int i, len, groups, bg_per_blk, blkgrps, mult;
338 int nlinks = 0;
339 int error = 0;
340 char *cp, *ncp, *path = NULL, *buf = NULL;
341 char namebuf[MAXPATHLEN+1];
342 char c;
343
344 /* allocate file system specific data structure */
345 fp = malloc(sizeof(struct file));
346 if (fp == NULL)
347 return (ENOMEM);
348 bzero(fp, sizeof(struct file));
349 f->f_fsdata = (void *)fp;
350
351 /* allocate space and read super block */
352 fs = (struct ext2fs *)malloc(sizeof(*fs));
353 fp->f_fs = fs;
354 twiddle(1);
355 error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
356 EXT2_SBLOCK, EXT2_SBSIZE, (char *)fs, &buf_size);
357 if (error)
358 goto out;
359
360 if (buf_size != EXT2_SBSIZE || fs->fs_magic != EXT2_MAGIC) {
361 error = EINVAL;
362 goto out;
363 }
364
365 /*
366 * compute in-core values for the superblock
367 */
368 fs->fs_bshift = EXT2_MINBSHIFT + fs->fs_fd.fd_bsize;
369 fs->fs_bsize = 1 << fs->fs_bshift;
370 fs->fs_bmask = fs->fs_bsize - 1;
371
372 fs->fs_fshift = EXT2_MINFSHIFT + fs->fs_fd.fd_fsize;
373 fs->fs_fsize = 1 << fs->fs_fshift;
374 fs->fs_fmask = fs->fs_fsize - 1;
375
376 if (fs->fs_revision == EXT2_REV0) {
377 fs->fs_isize = EXT2_R0_ISIZE;
378 fs->fs_firstino = EXT2_R0_FIRSTINO;
379 } else {
380 fs->fs_isize = fs->fs_fd.fd_isize;
381 fs->fs_firstino = fs->fs_fd.fd_firstino;
382 }
383 fs->fs_imask = fs->fs_isize - 1;
384 fs->fs_ipb = fs->fs_bsize / fs->fs_isize;
385 fs->fs_fsbtodb = (fs->fs_bsize / DEV_BSIZE) - 1;
386
387 /*
388 * we have to load in the "group descriptors" here
389 */
390 groups = howmany(fs->fs_blocks - fs->fs_firstblk, fs->fs_bpg);
391 bg_per_blk = fs->fs_bsize / sizeof(struct ext2blkgrp);
392 blkgrps = howmany(groups, bg_per_blk);
393 len = blkgrps * fs->fs_bsize;
394
395 fp->f_bg = malloc(len);
396 twiddle(1);
397 error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
398 EXT2_SBLOCK + EXT2_SBSIZE / DEV_BSIZE, len,
399 (char *)fp->f_bg, &buf_size);
400 if (error)
401 goto out;
402
403 /*
404 * XXX
405 * validation of values? (blocksize, descriptors, etc?)
406 */
407
408 /*
409 * Calculate indirect block levels.
410 */
411 mult = 1;
412 for (i = 0; i < EXT2_NIADDR; i++) {
413 mult *= nindir(fs);
414 fp->f_nindir[i] = mult;
415 }
416
417 inumber = EXT2_ROOTINO;
418 if ((error = read_inode(inumber, f)) != 0)
419 goto out;
420
421 path = strdup(upath);
422 if (path == NULL) {
423 error = ENOMEM;
424 goto out;
425 }
426 cp = path;
427 while (*cp) {
428 /*
429 * Remove extra separators
430 */
431 while (*cp == '/')
432 cp++;
433 if (*cp == '\0')
434 break;
435
436 /*
437 * Check that current node is a directory.
438 */
439 if (! S_ISDIR(fp->f_di.di_mode)) {
440 error = ENOTDIR;
441 goto out;
442 }
443
444 /*
445 * Get next component of path name.
446 */
447 len = 0;
448
449 ncp = cp;
450 while ((c = *cp) != '\0' && c != '/') {
451 if (++len > EXT2_MAXNAMLEN) {
452 error = ENOENT;
453 goto out;
454 }
455 cp++;
456 }
457 *cp = '\0';
458
459 /*
460 * Look up component in current directory.
461 * Save directory inumber in case we find a
462 * symbolic link.
463 */
464 parent_inumber = inumber;
465 error = search_directory(ncp, f, &inumber);
466 *cp = c;
467 if (error)
468 goto out;
469
470 /*
471 * Open next component.
472 */
473 if ((error = read_inode(inumber, f)) != 0)
474 goto out;
475
476 /*
477 * Check for symbolic link.
478 */
479 if (S_ISLNK(fp->f_di.di_mode)) {
480 int link_len = fp->f_di.di_size;
481 int len;
482
483 len = strlen(cp);
484 if (link_len + len > MAXPATHLEN ||
485 ++nlinks > MAXSYMLINKS) {
486 error = ENOENT;
487 goto out;
488 }
489
490 bcopy(cp, &namebuf[link_len], len + 1);
491 if (fp->f_di.di_nblk == 0) {
492 bcopy(fp->f_di.di_shortlink,
493 namebuf, link_len);
494 } else {
495 /*
496 * Read file for symbolic link
497 */
498 struct ext2fs *fs = fp->f_fs;
499 daddr_t disk_block;
500 size_t buf_size;
501
502 if (! buf)
503 buf = malloc(fs->fs_bsize);
504 error = block_map(f, (daddr_t)0, &disk_block);
505 if (error)
506 goto out;
507
508 twiddle(1);
509 error = (f->f_dev->dv_strategy)(f->f_devdata,
510 F_READ, fsb_to_db(fs, disk_block),
511 fs->fs_bsize, buf, &buf_size);
512 if (error)
513 goto out;
514
515 bcopy((char *)buf, namebuf, link_len);
516 }
517
518 /*
519 * If relative pathname, restart at parent directory.
520 * If absolute pathname, restart at root.
521 */
522 cp = namebuf;
523 if (*cp != '/')
524 inumber = parent_inumber;
525 else
526 inumber = (ino_t)EXT2_ROOTINO;
527
528 if ((error = read_inode(inumber, f)) != 0)
529 goto out;
530 }
531 }
532
533 /*
534 * Found terminal component.
535 */
536 error = 0;
537 fp->f_seekp = 0;
538 out:
539 if (buf)
540 free(buf);
541 if (path)
542 free(path);
543 if (error) {
544 if (fp->f_buf)
545 free(fp->f_buf);
546 free(fp->f_fs);
547 free(fp);
548 }
549 return (error);
550 }
551
552 /*
553 * Read a new inode into a file structure.
554 */
555 static int
read_inode(ino_t inumber,struct open_file * f)556 read_inode(ino_t inumber, struct open_file *f)
557 {
558 struct file *fp = (struct file *)f->f_fsdata;
559 struct ext2fs *fs = fp->f_fs;
560 struct ext2dinode *dp;
561 char *buf;
562 size_t rsize;
563 int level, error = 0;
564
565 /*
566 * Read inode and save it.
567 */
568 buf = malloc(fs->fs_bsize);
569 twiddle(1);
570 error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
571 ino_to_db(fs, fp->f_bg, inumber), fs->fs_bsize, buf, &rsize);
572 if (error)
573 goto out;
574 if (rsize != fs->fs_bsize) {
575 error = EIO;
576 goto out;
577 }
578
579 dp = (struct ext2dinode *)buf;
580 fp->f_di = dp[ino_to_bo(fs, inumber)];
581
582 /* clear out old buffers */
583 for (level = 0; level < EXT2_NIADDR; level++)
584 fp->f_blkno[level] = -1;
585 fp->f_buf_blkno = -1;
586 fp->f_seekp = 0;
587
588 out:
589 free(buf);
590 return (error);
591 }
592
593 /*
594 * Given an offset in a file, find the disk block number that
595 * contains that block.
596 */
597 static int
block_map(struct open_file * f,daddr_t file_block,daddr_t * disk_block_p)598 block_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p)
599 {
600 struct file *fp = (struct file *)f->f_fsdata;
601 struct ext2fs *fs = fp->f_fs;
602 daddr_t ind_block_num;
603 int32_t *ind_p;
604 int idx, level;
605 int error;
606
607 /*
608 * Index structure of an inode:
609 *
610 * di_db[0..EXT2_NDADDR-1] hold block numbers for blocks
611 * 0..EXT2_NDADDR-1
612 *
613 * di_ib[0] index block 0 is the single indirect block
614 * holds block numbers for blocks
615 * EXT2_NDADDR .. EXT2_NDADDR + NINDIR(fs)-1
616 *
617 * di_ib[1] index block 1 is the double indirect block
618 * holds block numbers for INDEX blocks for blocks
619 * EXT2_NDADDR + NINDIR(fs) ..
620 * EXT2_NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
621 *
622 * di_ib[2] index block 2 is the triple indirect block
623 * holds block numbers for double-indirect
624 * blocks for blocks
625 * EXT2_NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
626 * EXT2_NDADDR + NINDIR(fs) + NINDIR(fs)**2
627 * + NINDIR(fs)**3 - 1
628 */
629
630 if (file_block < EXT2_NDADDR) {
631 /* Direct block. */
632 *disk_block_p = fp->f_di.di_db[file_block];
633 return (0);
634 }
635
636 file_block -= EXT2_NDADDR;
637
638 /*
639 * nindir[0] = NINDIR
640 * nindir[1] = NINDIR**2
641 * nindir[2] = NINDIR**3
642 * etc
643 */
644 for (level = 0; level < EXT2_NIADDR; level++) {
645 if (file_block < fp->f_nindir[level])
646 break;
647 file_block -= fp->f_nindir[level];
648 }
649 if (level == EXT2_NIADDR) {
650 /* Block number too high */
651 return (EFBIG);
652 }
653
654 ind_block_num = fp->f_di.di_ib[level];
655
656 for (; level >= 0; level--) {
657 if (ind_block_num == 0) {
658 *disk_block_p = 0; /* missing */
659 return (0);
660 }
661
662 if (fp->f_blkno[level] != ind_block_num) {
663 if (fp->f_blk[level] == (char *)0)
664 fp->f_blk[level] =
665 malloc(fs->fs_bsize);
666 twiddle(1);
667 error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
668 fsb_to_db(fp->f_fs, ind_block_num), fs->fs_bsize,
669 fp->f_blk[level], &fp->f_blksize[level]);
670 if (error)
671 return (error);
672 if (fp->f_blksize[level] != fs->fs_bsize)
673 return (EIO);
674 fp->f_blkno[level] = ind_block_num;
675 }
676
677 ind_p = (int32_t *)fp->f_blk[level];
678
679 if (level > 0) {
680 idx = file_block / fp->f_nindir[level - 1];
681 file_block %= fp->f_nindir[level - 1];
682 } else {
683 idx = file_block;
684 }
685 ind_block_num = ind_p[idx];
686 }
687
688 *disk_block_p = ind_block_num;
689
690 return (0);
691 }
692
693 /*
694 * Read a portion of a file into an internal buffer. Return
695 * the location in the buffer and the amount in the buffer.
696 */
697 static int
buf_read_file(struct open_file * f,char ** buf_p,size_t * size_p)698 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
699 {
700 struct file *fp = (struct file *)f->f_fsdata;
701 struct ext2fs *fs = fp->f_fs;
702 long off;
703 daddr_t file_block;
704 daddr_t disk_block;
705 size_t block_size;
706 int error = 0;
707
708 off = blkoff(fs, fp->f_seekp);
709 file_block = lblkno(fs, fp->f_seekp);
710 block_size = dblksize(fs, &fp->f_di, file_block);
711
712 if (file_block != fp->f_buf_blkno) {
713 error = block_map(f, file_block, &disk_block);
714 if (error)
715 goto done;
716
717 if (fp->f_buf == (char *)0)
718 fp->f_buf = malloc(fs->fs_bsize);
719
720 if (disk_block == 0) {
721 bzero(fp->f_buf, block_size);
722 fp->f_buf_size = block_size;
723 } else {
724 twiddle(4);
725 error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
726 fsb_to_db(fs, disk_block), block_size,
727 fp->f_buf, &fp->f_buf_size);
728 if (error)
729 goto done;
730 }
731 fp->f_buf_blkno = file_block;
732 }
733
734 /*
735 * Return address of byte in buffer corresponding to
736 * offset, and size of remainder of buffer after that
737 * byte.
738 */
739 *buf_p = fp->f_buf + off;
740 *size_p = block_size - off;
741
742 /*
743 * But truncate buffer at end of file.
744 */
745 if (*size_p > fp->f_di.di_size - fp->f_seekp)
746 *size_p = fp->f_di.di_size - fp->f_seekp;
747 done:
748 return (error);
749 }
750
751 /*
752 * Search a directory for a name and return its
753 * i_number.
754 */
755 static int
search_directory(char * name,struct open_file * f,ino_t * inumber_p)756 search_directory(char *name, struct open_file *f, ino_t *inumber_p)
757 {
758 struct file *fp = (struct file *)f->f_fsdata;
759 struct ext2dirent *dp, *edp;
760 char *buf;
761 size_t buf_size;
762 int namlen, length;
763 int error;
764
765 length = strlen(name);
766 fp->f_seekp = 0;
767 while (fp->f_seekp < fp->f_di.di_size) {
768 error = buf_read_file(f, &buf, &buf_size);
769 if (error)
770 return (error);
771 dp = (struct ext2dirent *)buf;
772 edp = (struct ext2dirent *)(buf + buf_size);
773 while (dp < edp) {
774 if (dp->d_ino == (ino_t)0)
775 goto next;
776 namlen = dp->d_namlen;
777 if (namlen == length &&
778 strncmp(name, dp->d_name, length) == 0) {
779 /* found entry */
780 *inumber_p = dp->d_ino;
781 return (0);
782 }
783 next:
784 dp = (struct ext2dirent *)((char *)dp + dp->d_reclen);
785 }
786 fp->f_seekp += buf_size;
787 }
788 return (ENOENT);
789 }
790
791 static int
ext2fs_close(struct open_file * f)792 ext2fs_close(struct open_file *f)
793 {
794 struct file *fp = (struct file *)f->f_fsdata;
795 int level;
796
797 if (fp == NULL)
798 return (0);
799
800 f->f_fsdata = NULL;
801
802 for (level = 0; level < EXT2_NIADDR; level++) {
803 if (fp->f_blk[level])
804 free(fp->f_blk[level]);
805 }
806 if (fp->f_buf)
807 free(fp->f_buf);
808 if (fp->f_bg)
809 free(fp->f_bg);
810 free(fp->f_fs);
811 free(fp);
812 return (0);
813 }
814
815 static int
ext2fs_read(struct open_file * f,void * addr,size_t size,size_t * resid)816 ext2fs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
817 {
818 struct file *fp = (struct file *)f->f_fsdata;
819 size_t csize, buf_size;
820 char *buf;
821 int error = 0;
822
823 while (size != 0) {
824 if (fp->f_seekp >= fp->f_di.di_size)
825 break;
826
827 error = buf_read_file(f, &buf, &buf_size);
828 if (error)
829 break;
830
831 csize = size;
832 if (csize > buf_size)
833 csize = buf_size;
834
835 bcopy(buf, addr, csize);
836
837 fp->f_seekp += csize;
838 addr = (char *)addr + csize;
839 size -= csize;
840 }
841 if (resid)
842 *resid = size;
843 return (error);
844 }
845
846 static off_t
ext2fs_seek(struct open_file * f,off_t offset,int where)847 ext2fs_seek(struct open_file *f, off_t offset, int where)
848 {
849 struct file *fp = (struct file *)f->f_fsdata;
850
851 switch (where) {
852 case SEEK_SET:
853 fp->f_seekp = offset;
854 break;
855 case SEEK_CUR:
856 fp->f_seekp += offset;
857 break;
858 case SEEK_END:
859 fp->f_seekp = fp->f_di.di_size - offset;
860 break;
861 default:
862 errno = EINVAL;
863 return (-1);
864 }
865 return (fp->f_seekp);
866 }
867
868 static int
ext2fs_stat(struct open_file * f,struct stat * sb)869 ext2fs_stat(struct open_file *f, struct stat *sb)
870 {
871 struct file *fp = (struct file *)f->f_fsdata;
872
873 /* only important stuff */
874 sb->st_mode = fp->f_di.di_mode;
875 sb->st_uid = fp->f_di.di_uid;
876 sb->st_gid = fp->f_di.di_gid;
877 sb->st_size = fp->f_di.di_size;
878 return (0);
879 }
880
881 static int
ext2fs_readdir(struct open_file * f,struct dirent * d)882 ext2fs_readdir(struct open_file *f, struct dirent *d)
883 {
884 struct file *fp = (struct file *)f->f_fsdata;
885 struct ext2dirent *ed;
886 char *buf;
887 size_t buf_size;
888 int error;
889
890 /*
891 * assume that a directory entry will not be split across blocks
892 */
893
894 do {
895 if (fp->f_seekp >= fp->f_di.di_size)
896 return (ENOENT);
897 error = buf_read_file(f, &buf, &buf_size);
898 if (error)
899 return (error);
900 ed = (struct ext2dirent *)buf;
901 fp->f_seekp += ed->d_reclen;
902 } while (ed->d_ino == (ino_t)0);
903
904 d->d_type = EXTFTODT(ed->d_type);
905 strncpy(d->d_name, ed->d_name, ed->d_namlen);
906 d->d_name[ed->d_namlen] = '\0';
907 return (0);
908 }
909