]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libstand/nandfs.c
Import byacc from invisible island, it brings us lots of compatibilities with
[FreeBSD/FreeBSD.git] / lib / libstand / nandfs.c
1 /*-
2  * Copyright (c) 2010-2012 Semihalf.
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 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/queue.h>
32 #include <sys/stdint.h>
33 #include <ufs/ufs/dinode.h>
34 #include <fs/nandfs/nandfs_fs.h>
35 #include "stand.h"
36 #include "string.h"
37 #include "zlib.h"
38
39 #define DEBUG
40 #undef DEBUG
41 #ifdef DEBUG
42 #define NANDFS_DEBUG(fmt, args...) do { \
43     printf("NANDFS_DEBUG:" fmt "\n", ##args); } while (0)
44 #else
45 #define NANDFS_DEBUG(fmt, args...)
46 #endif
47
48 struct nandfs_mdt {
49         uint32_t        entries_per_block;
50         uint32_t        entries_per_group;
51         uint32_t        blocks_per_group;
52         uint32_t        groups_per_desc_block;  /* desc is super group */
53         uint32_t        blocks_per_desc_block;  /* desc is super group */
54 };
55
56 struct bmap_buf {
57         LIST_ENTRY(bmap_buf)    list;
58         nandfs_daddr_t          blknr;
59         uint64_t                *map;
60 };
61
62 struct nandfs_node {
63         struct nandfs_inode     *inode;
64         LIST_HEAD(, bmap_buf)   bmap_bufs;
65 };
66 struct nandfs {
67         int     nf_blocksize;
68         int     nf_sectorsize;
69         int     nf_cpno;
70
71         struct open_file        *nf_file;
72         struct nandfs_node      *nf_opened_node;
73         u_int                   nf_offset;
74         uint8_t                 *nf_buf;
75         int64_t                 nf_buf_blknr;
76
77         struct nandfs_fsdata            *nf_fsdata;
78         struct nandfs_super_block       *nf_sb;
79         struct nandfs_segment_summary   nf_segsum;
80         struct nandfs_checkpoint        nf_checkpoint;
81         struct nandfs_super_root        nf_sroot;
82         struct nandfs_node              nf_ifile;
83         struct nandfs_node              nf_datfile;
84         struct nandfs_node              nf_cpfile;
85         struct nandfs_mdt               nf_datfile_mdt;
86         struct nandfs_mdt               nf_ifile_mdt;
87
88         int nf_nindir[NIADDR];
89 };
90
91 static int nandfs_open(const char *, struct open_file *);
92 static int nandfs_close(struct open_file *);
93 static int nandfs_read(struct open_file *, void *, size_t, size_t *);
94 static off_t nandfs_seek(struct open_file *, off_t, int);
95 static int nandfs_stat(struct open_file *, struct stat *);
96 static int nandfs_readdir(struct open_file *, struct dirent *);
97
98 static int nandfs_buf_read(struct nandfs *, char **, size_t *);
99 static struct nandfs_node *nandfs_lookup_inode(struct nandfs *, nandfs_daddr_t);
100 static struct nandfs_node *nandfs_lookup_path(struct nandfs *, const char *);
101 static int nandfs_read_inode(struct nandfs *, struct nandfs_node *,
102     nandfs_lbn_t, u_int, void *, int);
103 static int nandfs_read_blk(struct nandfs *, nandfs_daddr_t, void *, int);
104 static int nandfs_bmap_lookup(struct nandfs *, struct nandfs_node *,
105     nandfs_lbn_t, nandfs_daddr_t *, int);
106 static int nandfs_get_checkpoint(struct nandfs *, uint64_t,
107     struct nandfs_checkpoint *);
108 static nandfs_daddr_t nandfs_vtop(struct nandfs *, nandfs_daddr_t);
109 static void nandfs_calc_mdt_consts(int, struct nandfs_mdt *, int);
110 static void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t,
111     nandfs_daddr_t *, uint32_t *);
112 static int ioread(struct open_file *, off_t, void *, u_int);
113 static int nandfs_probe_sectorsize(struct open_file *);
114
115 struct fs_ops nandfs_fsops = {
116         "nandfs",
117         nandfs_open,
118         nandfs_close,
119         nandfs_read,
120         null_write,
121         nandfs_seek,
122         nandfs_stat,
123         nandfs_readdir
124 };
125
126 #define NINDIR(fs)      ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
127
128 static int
129 nandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata)
130 {
131         uint32_t fsdata_crc, comp_crc;
132
133         if (fsdata->f_magic != NANDFS_FSDATA_MAGIC)
134                 return (0);
135
136         /* Preserve crc */
137         fsdata_crc = fsdata->f_sum;
138
139         /* Calculate */
140         fsdata->f_sum = (0);
141         comp_crc = crc32(0, (uint8_t *)fsdata, fsdata->f_bytes);
142
143         /* Restore */
144         fsdata->f_sum = fsdata_crc;
145
146         /* Check CRC */
147         return (fsdata_crc == comp_crc);
148 }
149
150 static int
151 nandfs_check_superblock_crc(struct nandfs_fsdata *fsdata,
152     struct nandfs_super_block *super)
153 {
154         uint32_t super_crc, comp_crc;
155
156         /* Check super block magic */
157         if (super->s_magic != NANDFS_SUPER_MAGIC)
158                 return (0);
159
160         /* Preserve CRC */
161         super_crc = super->s_sum;
162
163         /* Calculate */
164         super->s_sum = (0);
165         comp_crc = crc32(0, (uint8_t *)super, fsdata->f_sbbytes);
166
167         /* Restore */
168         super->s_sum = super_crc;
169
170         /* Check CRC */
171         return (super_crc == comp_crc);
172 }
173
174 static int
175 nandfs_find_super_block(struct nandfs *fs, struct open_file *f)
176 {
177         struct nandfs_super_block *sb;
178         int i, j, n;
179         int sectors_to_read, error;
180
181         sb = malloc(fs->nf_sectorsize);
182         if (sb == NULL)
183                 return (ENOMEM);
184
185         memset(fs->nf_sb, 0, sizeof(*fs->nf_sb));
186
187         sectors_to_read = (NANDFS_NFSAREAS * fs->nf_fsdata->f_erasesize) /
188             fs->nf_sectorsize;
189         for (i = 0; i < sectors_to_read; i++) {
190                 NANDFS_DEBUG("reading i %d offset %d\n", i,
191                     i * fs->nf_sectorsize);
192                 error = ioread(f, i * fs->nf_sectorsize, (char *)sb,
193                     fs->nf_sectorsize);
194                 if (error) {
195                         NANDFS_DEBUG("error %d\n", error);
196                         continue;
197                 }
198                 n = fs->nf_sectorsize / sizeof(struct nandfs_super_block);
199                 if ((i * fs->nf_sectorsize) % fs->nf_fsdata->f_erasesize == 0) {
200                         if (fs->nf_sectorsize == sizeof(struct nandfs_fsdata))
201                                 continue;
202                         else {
203                                 sb += (sizeof(struct nandfs_fsdata) /
204                                     sizeof(struct nandfs_super_block));
205                                 n -= (sizeof(struct nandfs_fsdata) /
206                                     sizeof(struct nandfs_super_block));
207                         }
208                 }
209
210                 for (j = 0; j < n; j++) {
211                         if (!nandfs_check_superblock_crc(fs->nf_fsdata, &sb[j]))
212                                 continue;
213                         NANDFS_DEBUG("magic %x wtime %jd\n", sb->s_magic,
214                             sb->s_wtime);
215                         if (sb[j].s_wtime > fs->nf_sb->s_wtime)
216                                 memcpy(fs->nf_sb, &sb[j], sizeof(*fs->nf_sb));
217                 }
218         }
219
220         free(sb);
221
222         return (fs->nf_sb->s_magic != 0 ? 0 : EINVAL);
223 }
224
225 static int
226 nandfs_find_fsdata(struct nandfs *fs, struct open_file *f)
227 {
228         int offset, error, i;
229
230         NANDFS_DEBUG("starting\n");
231
232         offset = 0;
233         for (i = 0; i < 64 * NANDFS_NFSAREAS; i++) {
234                 error = ioread(f, offset, (char *)fs->nf_fsdata,
235                     sizeof(struct nandfs_fsdata));
236                 if (error)
237                         return (error);
238                 if (fs->nf_fsdata->f_magic == NANDFS_FSDATA_MAGIC) {
239                         NANDFS_DEBUG("found at %x, volume %s\n", offset,
240                             fs->nf_fsdata->f_volume_name);
241                         if (nandfs_check_fsdata_crc(fs->nf_fsdata))
242                                 break;
243                 }
244                 offset += fs->nf_sectorsize;
245         }
246
247         return (error);
248 }
249
250 static int
251 nandfs_read_structures(struct nandfs *fs, struct open_file *f)
252 {
253         int error;
254
255         error = nandfs_find_fsdata(fs, f);
256         if (error)
257                 return (error);
258
259         error = nandfs_find_super_block(fs, f);
260
261         if (error == 0)
262                 NANDFS_DEBUG("selected sb with w_time %jd last_pseg %jx\n",
263                     fs->nf_sb->s_wtime, fs->nf_sb->s_last_pseg);
264
265         return (error);
266 }
267
268 static int
269 nandfs_mount(struct nandfs *fs, struct open_file *f)
270 {
271         int err = 0, level;
272         uint64_t last_pseg;
273
274         fs->nf_fsdata = malloc(sizeof(struct nandfs_fsdata));
275         fs->nf_sb = malloc(sizeof(struct nandfs_super_block));
276
277         err = nandfs_read_structures(fs, f);
278         if (err) {
279                 free(fs->nf_fsdata);
280                 free(fs->nf_sb);
281                 return (err);
282         }
283
284         fs->nf_blocksize = 1 << (fs->nf_fsdata->f_log_block_size + 10);
285
286         NANDFS_DEBUG("using superblock with wtime %jd\n", fs->nf_sb->s_wtime);
287
288         fs->nf_cpno = fs->nf_sb->s_last_cno;
289         last_pseg = fs->nf_sb->s_last_pseg;
290
291         /*
292          * Calculate indirect block levels.
293          */
294         nandfs_daddr_t mult;
295
296         mult = 1;
297         for (level = 0; level < NIADDR; level++) {
298                 mult *= NINDIR(fs);
299                 fs->nf_nindir[level] = mult;
300         }
301
302         nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_datfile_mdt,
303             fs->nf_fsdata->f_dat_entry_size);
304
305         nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_ifile_mdt,
306             fs->nf_fsdata->f_inode_size);
307
308         err = ioread(f, last_pseg * fs->nf_blocksize, &fs->nf_segsum,
309             sizeof(struct nandfs_segment_summary));
310         if (err) {
311                 free(fs->nf_sb);
312                 free(fs->nf_fsdata);
313                 return (err);
314         }
315
316         err = ioread(f, (last_pseg + fs->nf_segsum.ss_nblocks - 1) *
317             fs->nf_blocksize, &fs->nf_sroot, sizeof(struct nandfs_super_root));
318         if (err) {
319                 free(fs->nf_sb);
320                 free(fs->nf_fsdata);
321                 return (err);
322         }
323
324         fs->nf_datfile.inode = &fs->nf_sroot.sr_dat;
325         LIST_INIT(&fs->nf_datfile.bmap_bufs);
326         fs->nf_cpfile.inode = &fs->nf_sroot.sr_cpfile;
327         LIST_INIT(&fs->nf_cpfile.bmap_bufs);
328
329         err = nandfs_get_checkpoint(fs, fs->nf_cpno, &fs->nf_checkpoint);
330         if (err) {
331                 free(fs->nf_sb);
332                 free(fs->nf_fsdata);
333                 return (err);
334         }
335
336         NANDFS_DEBUG("checkpoint cp_cno=%lld\n", fs->nf_checkpoint.cp_cno);
337         NANDFS_DEBUG("checkpoint cp_inodes_count=%lld\n",
338             fs->nf_checkpoint.cp_inodes_count);
339         NANDFS_DEBUG("checkpoint cp_ifile_inode.i_blocks=%lld\n",
340             fs->nf_checkpoint.cp_ifile_inode.i_blocks);
341
342         fs->nf_ifile.inode = &fs->nf_checkpoint.cp_ifile_inode;
343         LIST_INIT(&fs->nf_ifile.bmap_bufs);
344         return (0);
345 }
346
347 #define NINDIR(fs)      ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
348
349 static int
350 nandfs_open(const char *path, struct open_file *f)
351 {
352         struct nandfs *fs;
353         struct nandfs_node *node;
354         int err, bsize, level;
355
356         NANDFS_DEBUG("nandfs_open('%s', %p)\n", path, f);
357
358         fs = malloc(sizeof(struct nandfs));
359         f->f_fsdata = fs;
360         fs->nf_file = f;
361
362         bsize = nandfs_probe_sectorsize(f);
363         if (bsize < 0) {
364                 printf("Cannot probe medium sector size\n");
365                 return (EINVAL);
366         }
367
368         fs->nf_sectorsize = bsize;
369
370         /*
371          * Calculate indirect block levels.
372          */
373         nandfs_daddr_t mult;
374
375         mult = 1;
376         for (level = 0; level < NIADDR; level++) {
377                 mult *= NINDIR(fs);
378                 fs->nf_nindir[level] = mult;
379         }
380
381         NANDFS_DEBUG("fs %p nf_sectorsize=%x\n", fs, fs->nf_sectorsize);
382
383         err = nandfs_mount(fs, f);
384         if (err) {
385                 NANDFS_DEBUG("Cannot mount nandfs: %s\n", strerror(err));
386                 return (err);
387         }
388
389         node = nandfs_lookup_path(fs, path);
390         if (node == NULL)
391                 return (EINVAL);
392
393         fs->nf_offset = 0;
394         fs->nf_buf = NULL;
395         fs->nf_buf_blknr = -1;
396         fs->nf_opened_node = node;
397         LIST_INIT(&fs->nf_opened_node->bmap_bufs);
398         return (0);
399 }
400
401 static int
402 nandfs_free_node(struct nandfs_node *node)
403 {
404         struct bmap_buf *bmap, *tmp;
405
406         free(node->inode);
407         LIST_FOREACH_SAFE(bmap, &node->bmap_bufs, list, tmp) {
408                 LIST_REMOVE(bmap, list);
409                 free(bmap->map);
410                 free(bmap);
411         }
412         free(node);
413 }
414
415 static int
416 nandfs_close(struct open_file *f)
417 {
418         struct nandfs *fs = f->f_fsdata;
419
420         NANDFS_DEBUG("nandfs_close(%p)\n", f);
421
422         if (fs->nf_buf != NULL)
423                 free(fs->nf_buf);
424
425         nandfs_free_node(fs->nf_opened_node);
426         free(fs->nf_sb);
427         free(fs);
428 }
429
430 static int
431 nandfs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
432 {
433         struct nandfs *fs = (struct nandfs *)f->f_fsdata;
434         size_t csize, buf_size;
435         uint8_t *buf;
436         int error = 0;
437
438         NANDFS_DEBUG("nandfs_read(file=%p, addr=%p, size=%d)\n", f, addr, size);
439
440         while (size != 0) {
441                 if (fs->nf_offset >= fs->nf_opened_node->inode->i_size)
442                         break;
443
444                 error = nandfs_buf_read(fs, (void *)&buf, &buf_size);
445                 if (error)
446                         break;
447
448                 csize = size;
449                 if (csize > buf_size)
450                         csize = buf_size;
451
452                 bcopy(buf, addr, csize);
453
454                 fs->nf_offset += csize;
455                 addr = (char *)addr + csize;
456                 size -= csize;
457         }
458
459         if (resid)
460                 *resid = size;
461         return (error);
462 }
463
464 static off_t
465 nandfs_seek(struct open_file *f, off_t offset, int where)
466 {
467         struct nandfs *fs = f->f_fsdata;
468         off_t off;
469         u_int size;
470
471         NANDFS_DEBUG("nandfs_seek(file=%p, offset=%lld, where=%d)\n", f,
472             offset, where);
473
474         size = fs->nf_opened_node->inode->i_size;
475
476         switch (where) {
477         case SEEK_SET:
478                 off = 0;
479                 break;
480         case SEEK_CUR:
481                 off = fs->nf_offset;
482                 break;
483         case SEEK_END:
484                 off = size;
485                 break;
486         default:
487                 errno = EINVAL;
488                 return (-1);
489         }
490
491         off += offset;
492         if (off < 0 || off > size) {
493                 errno = EINVAL;
494                 return(-1);
495         }
496
497         fs->nf_offset = (u_int)off;
498
499         return (off);
500 }
501
502 static int
503 nandfs_stat(struct open_file *f, struct stat *sb)
504 {
505         struct nandfs *fs = f->f_fsdata;
506
507         NANDFS_DEBUG("nandfs_stat(file=%p, stat=%p)\n", f, sb);
508
509         sb->st_size = fs->nf_opened_node->inode->i_size;
510         sb->st_mode = fs->nf_opened_node->inode->i_mode;
511         sb->st_uid = fs->nf_opened_node->inode->i_uid;
512         sb->st_gid = fs->nf_opened_node->inode->i_gid;
513         return (0);
514 }
515
516 static int
517 nandfs_readdir(struct open_file *f, struct dirent *d)
518 {
519         struct nandfs *fs = f->f_fsdata;
520         struct nandfs_dir_entry *dirent;
521         uint8_t *buf;
522         size_t buf_size;
523
524         NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)\n", f, d);
525
526         if (fs->nf_offset >= fs->nf_opened_node->inode->i_size) {
527                 NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) ENOENT\n",
528                     f, d);
529                 return (ENOENT);
530         }
531
532         if (nandfs_buf_read(fs, (void *)&buf, &buf_size)) {
533                 NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)"
534                     "buf_read failed\n", f, d);
535                 return (EIO);
536         }
537
538         NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) moving forward\n",
539             f, d);
540
541         dirent = (struct nandfs_dir_entry *)buf;
542         fs->nf_offset += dirent->rec_len;
543         strncpy(d->d_name, dirent->name, dirent->name_len);
544         d->d_name[dirent->name_len] = '\0';
545         d->d_type = dirent->file_type;
546         return (0);
547 }
548
549 static int
550 nandfs_buf_read(struct nandfs *fs, char **buf_p, size_t *size_p)
551 {
552         nandfs_daddr_t blknr, blkoff;
553
554         blknr = fs->nf_offset / fs->nf_blocksize;
555         blkoff = fs->nf_offset % fs->nf_blocksize;
556
557         if (blknr != fs->nf_buf_blknr) {
558                 if (fs->nf_buf == NULL)
559                         fs->nf_buf = malloc(fs->nf_blocksize);
560
561                 if (nandfs_read_inode(fs, fs->nf_opened_node, blknr, 1,
562                     fs->nf_buf, 0))
563                         return (EIO);
564
565                 fs->nf_buf_blknr = blknr;
566         }
567
568         *buf_p = fs->nf_buf + blkoff;
569         *size_p = fs->nf_blocksize - blkoff;
570
571         NANDFS_DEBUG("nandfs_buf_read buf_p=%p size_p=%d\n", *buf_p, *size_p);
572
573         if (*size_p > fs->nf_opened_node->inode->i_size - fs->nf_offset)
574                 *size_p = fs->nf_opened_node->inode->i_size - fs->nf_offset;
575
576         return (0);
577 }
578
579 static struct nandfs_node *
580 nandfs_lookup_node(struct nandfs *fs, uint64_t ino)
581 {
582         uint64_t blocknr;
583         int entrynr;
584         struct nandfs_inode *buffer;
585         struct nandfs_node *node;
586         struct nandfs_inode *inode;
587
588         NANDFS_DEBUG("nandfs_lookup_node ino=%lld\n", ino);
589
590         if (ino == 0) {
591                 printf("nandfs_lookup_node: invalid inode requested\n");
592                 return (NULL);
593         }
594
595         buffer = malloc(fs->nf_blocksize);
596         inode = malloc(sizeof(struct nandfs_inode));
597         node = malloc(sizeof(struct nandfs_node));
598
599         nandfs_mdt_trans(&fs->nf_ifile_mdt, ino, &blocknr, &entrynr);
600
601         if (nandfs_read_inode(fs, &fs->nf_ifile, blocknr, 1, buffer, 0))
602                 return (NULL);
603
604         memcpy(inode, &buffer[entrynr], sizeof(struct nandfs_inode));
605         node->inode = inode;
606         free(buffer);
607         return (node);
608 }
609
610 static struct nandfs_node *
611 nandfs_lookup_path(struct nandfs *fs, const char *path)
612 {
613         struct nandfs_node *node;
614         struct nandfs_dir_entry *dirent;
615         char *namebuf;
616         uint64_t i, j, done, counter, pinode, inode;
617         int nlinks = 0, len, link_len, nameidx;
618         uint8_t *buffer, *orig;
619         char *strp, *lpath;
620
621         buffer = malloc(fs->nf_blocksize);
622         orig = buffer;
623
624         namebuf = malloc(2 * MAXPATHLEN + 2);
625         strncpy(namebuf, path, MAXPATHLEN);
626         namebuf[MAXPATHLEN] = '\0';
627         done = nameidx = 0;
628         lpath = namebuf;
629
630         /* Get the root inode */
631         node = nandfs_lookup_node(fs, NANDFS_ROOT_INO);
632         inode = NANDFS_ROOT_INO;
633
634         while ((strp = strsep(&lpath, "/")) != NULL) {
635                 if (*strp == '\0')
636                         continue;
637                 if ((node->inode->i_mode & IFMT) != IFDIR) {
638                         nandfs_free_node(node);
639                         node = NULL;
640                         goto out;
641                 }
642
643                 len = strlen(strp);
644                 NANDFS_DEBUG("%s: looking for %s\n", __func__, strp);
645                 for (i = 0; i < node->inode->i_blocks; i++) {
646                         if (nandfs_read_inode(fs, node, i, 1, orig, 0)) {
647                                 node = NULL;
648                                 goto out;
649                         }
650
651                         buffer = orig;
652                         done = counter = 0;
653                         while (1) {
654                                 dirent = (struct nandfs_dir_entry *)buffer;
655                                 NANDFS_DEBUG("%s: dirent.name = %s\n",
656                                     __func__, dirent->name);
657                                 NANDFS_DEBUG("%s: dirent.rec_len = %d\n",
658                                     __func__, dirent->rec_len);
659                                 NANDFS_DEBUG("%s: dirent.inode = %lld\n",
660                                     __func__, dirent->inode);
661                                 if (len == dirent->name_len &&
662                                     (strncmp(strp, dirent->name, len) == 0) &&
663                                     dirent->inode != 0) {
664                                         nandfs_free_node(node);
665                                         node = nandfs_lookup_node(fs,
666                                             dirent->inode);
667                                         pinode = inode;
668                                         inode = dirent->inode;
669                                         done = 1;
670                                         break;
671                                 }
672
673                                 counter += dirent->rec_len;
674                                 buffer += dirent->rec_len;
675
676                                 if (counter == fs->nf_blocksize)
677                                         break;
678                         }
679
680                         if (done)
681                                 break;
682                 }
683
684                 if (!done) {
685                         node = NULL;
686                         goto out;
687                 }
688
689                 NANDFS_DEBUG("%s: %.*s has mode %o\n", __func__,
690                     dirent->name_len, dirent->name, node->inode->i_mode);
691
692                 if ((node->inode->i_mode & IFMT) == IFLNK) {
693                         NANDFS_DEBUG("%s: %.*s is symlink\n",
694                             __func__, dirent->name_len, dirent->name);
695                         link_len = node->inode->i_size;
696
697                         if (++nlinks > MAXSYMLINKS) {
698                                 nandfs_free_node(node);
699                                 node = NULL;
700                                 goto out;
701                         }
702
703                         if (nandfs_read_inode(fs, node, 0, 1, orig, 0)) {
704                                 nandfs_free_node(node);
705                                 node = NULL;
706                                 goto out;
707                         }
708
709                         NANDFS_DEBUG("%s: symlink is  %.*s\n",
710                             __func__, link_len, (char *)orig);
711
712                         nameidx = (nameidx == 0) ? MAXPATHLEN + 1 : 0;
713                         bcopy((char *)orig, namebuf + nameidx,
714                             (unsigned)link_len);
715                         if (lpath != NULL) {
716                                 namebuf[nameidx + link_len++] = '/';
717                                 strncpy(namebuf + nameidx + link_len, lpath,
718                                     MAXPATHLEN - link_len);
719                                 namebuf[nameidx + MAXPATHLEN] = '\0';
720                         } else
721                                 namebuf[nameidx + link_len] = '\0';
722
723                         NANDFS_DEBUG("%s: strp=%s, lpath=%s, namebuf0=%s, "
724                             "namebuf1=%s, idx=%d\n", __func__, strp, lpath,
725                             namebuf + 0, namebuf + MAXPATHLEN + 1, nameidx);
726
727                         lpath = namebuf + nameidx;
728
729                         nandfs_free_node(node);
730
731                         /*
732                          * If absolute pathname, restart at root. Otherwise
733                          * continue with out parent inode.
734                          */
735                         inode = (orig[0] == '/') ? NANDFS_ROOT_INO : pinode;
736                         node = nandfs_lookup_node(fs, inode);
737                 }
738         }
739
740 out:
741         free(namebuf);
742         free(orig);
743         return (node);
744 }
745
746 static int
747 nandfs_read_inode(struct nandfs *fs, struct nandfs_node *node,
748     nandfs_daddr_t blknr, u_int nblks, void *buf, int raw)
749 {
750         int i;
751         uint64_t *pblks;
752         uint64_t *vblks;
753         int error;
754
755         pblks = malloc(nblks * sizeof(uint64_t));
756         vblks = malloc(nblks * sizeof(uint64_t));
757
758         NANDFS_DEBUG("nandfs_read_inode fs=%p node=%p blknr=%lld nblks=%d\n",
759             fs, node, blknr, nblks);
760         for (i = 0; i < nblks; i++) {
761                 error = nandfs_bmap_lookup(fs, node, blknr + i, &vblks[i], raw);
762                 if (error) {
763                         free(pblks);
764                         free(vblks);
765                         return (error);
766                 }
767                 if (raw == 0)
768                         pblks[i] = nandfs_vtop(fs, vblks[i]);
769                 else
770                         pblks[i] = vblks[i];
771         }
772
773         for (i = 0; i < nblks; i++) {
774                 if (ioread(fs->nf_file, pblks[i] * fs->nf_blocksize, buf,
775                     fs->nf_blocksize)) {
776                         free(pblks);
777                         free(vblks);
778                         return (EIO);
779                 }
780
781                 buf += fs->nf_blocksize;
782         }
783
784         free(pblks);
785         free(vblks);
786         return (0);
787 }
788
789 static int
790 nandfs_read_blk(struct nandfs *fs, nandfs_daddr_t blknr, void *buf, int phys)
791 {
792         uint64_t pblknr;
793
794         pblknr = (phys ? blknr : nandfs_vtop(fs, blknr));
795
796         return (ioread(fs->nf_file, pblknr * fs->nf_blocksize, buf,
797             fs->nf_blocksize));
798 }
799
800 static int
801 nandfs_get_checkpoint(struct nandfs *fs, uint64_t cpno,
802     struct nandfs_checkpoint *cp)
803 {
804         uint64_t blocknr;
805         int blockoff, cp_per_block, dlen;
806         uint8_t *buf;
807
808         NANDFS_DEBUG("nandfs_get_checkpoint(fs=%p cpno=%lld)\n", fs, cpno);
809
810         buf = malloc(fs->nf_blocksize);
811
812         cpno += NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1;
813         dlen = fs->nf_fsdata->f_checkpoint_size;
814         cp_per_block = fs->nf_blocksize / dlen;
815         blocknr = cpno / cp_per_block;
816         blockoff = (cpno % cp_per_block) * dlen;
817
818         if (nandfs_read_inode(fs, &fs->nf_cpfile, blocknr, 1, buf, 0)) {
819                 free(buf);
820                 return (EINVAL);
821         }
822
823         memcpy(cp, buf + blockoff, sizeof(struct nandfs_checkpoint));
824         free(buf);
825
826         return (0);
827 }
828
829 static uint64_t *
830 nandfs_get_map(struct nandfs *fs, struct nandfs_node *node, nandfs_daddr_t blknr,
831     int phys)
832 {
833         struct bmap_buf *bmap;
834         uint64_t *map;
835
836         LIST_FOREACH(bmap, &node->bmap_bufs, list) {
837                 if (bmap->blknr == blknr)
838                         return (bmap->map);
839         }
840
841         map = malloc(fs->nf_blocksize);
842         if (nandfs_read_blk(fs, blknr, map, phys)) {
843                 free(map);
844                 return (NULL);
845         }
846
847         bmap = malloc(sizeof(struct bmap_buf));
848         bmap->blknr = blknr;
849         bmap->map = map;
850
851         LIST_INSERT_HEAD(&node->bmap_bufs, bmap, list);
852
853         NANDFS_DEBUG("%s:(node=%p, map=%p)\n", __func__, node, map);
854         return (map);
855 }
856
857 static int
858 nandfs_bmap_lookup(struct nandfs *fs, struct nandfs_node *node,
859     nandfs_lbn_t lblknr, nandfs_daddr_t *vblknr, int phys)
860 {
861         struct nandfs_inode *ino;
862         nandfs_daddr_t ind_block_num;
863         uint64_t *map, *indir;
864         uint64_t idx0, idx1, vblk, tmp;
865         int idx;
866         int level;
867
868         ino = node->inode;
869
870         if (lblknr < NDADDR) {
871                 *vblknr = ino->i_db[lblknr];
872                 return (0);
873         }
874
875         lblknr -= NDADDR;
876
877         /*
878          * nindir[0] = NINDIR
879          * nindir[1] = NINDIR**2
880          * nindir[2] = NINDIR**3
881          *      etc
882          */
883         for (level = 0; level < NIADDR; level++) {
884                 NANDFS_DEBUG("lblknr=%jx fs->nf_nindir[%d]=%d\n", lblknr, level, fs->nf_nindir[level]);
885                 if (lblknr < fs->nf_nindir[level])
886                         break;
887                 lblknr -= fs->nf_nindir[level];
888         }
889
890         if (level == NIADDR) {
891                 /* Block number too high */
892                 NANDFS_DEBUG("lblknr %jx too high\n", lblknr);
893                 return (EFBIG);
894         }
895
896         ind_block_num = ino->i_ib[level];
897
898         for (; level >= 0; level--) {
899                 if (ind_block_num == 0) {
900                         *vblknr = 0;    /* missing */
901                         return (0);
902                 }
903
904                 twiddle();
905                 NANDFS_DEBUG("calling get_map with %jx\n", ind_block_num);
906                 map = nandfs_get_map(fs, node, ind_block_num, phys);
907                 if (map == NULL)
908                         return (EIO);
909
910                 if (level > 0) {
911                         idx = lblknr / fs->nf_nindir[level - 1];
912                         lblknr %= fs->nf_nindir[level - 1];
913                 } else
914                         idx = lblknr;
915
916                 ind_block_num = ((nandfs_daddr_t *)map)[idx];
917         }
918
919         *vblknr = ind_block_num;
920
921         return (0);
922 }
923
924 static nandfs_daddr_t
925 nandfs_vtop(struct nandfs *fs, nandfs_daddr_t vblocknr)
926 {
927         nandfs_lbn_t blocknr;
928         nandfs_daddr_t pblocknr;
929         int entrynr;
930         struct nandfs_dat_entry *dat;
931
932         dat = malloc(fs->nf_blocksize);
933         nandfs_mdt_trans(&fs->nf_datfile_mdt, vblocknr, &blocknr, &entrynr);
934
935         if (nandfs_read_inode(fs, &fs->nf_datfile, blocknr, 1, dat, 1)) {
936                 free(dat);
937                 return (0);
938         }
939
940         NANDFS_DEBUG("nandfs_vtop entrynr=%d vblocknr=%lld pblocknr=%lld\n",
941             entrynr, vblocknr, dat[entrynr].de_blocknr);
942
943         pblocknr = dat[entrynr].de_blocknr;
944         free(dat);
945         return (pblocknr);
946 }
947
948 static void
949 nandfs_calc_mdt_consts(int blocksize, struct nandfs_mdt *mdt, int entry_size)
950 {
951
952         mdt->entries_per_group = blocksize * 8;    /* bits in sector */
953         mdt->entries_per_block = blocksize / entry_size;
954         mdt->blocks_per_group  =
955             (mdt->entries_per_group -1) / mdt->entries_per_block + 1 + 1;
956         mdt->groups_per_desc_block =
957             blocksize / sizeof(struct nandfs_block_group_desc);
958         mdt->blocks_per_desc_block =
959             mdt->groups_per_desc_block * mdt->blocks_per_group + 1;
960 }
961
962 static void
963 nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index,
964     nandfs_daddr_t *blocknr, uint32_t *entry_in_block)
965 {
966         nandfs_daddr_t blknr;
967         uint64_t group, group_offset, blocknr_in_group;
968         uint64_t desc_block, desc_offset;
969
970         /* Calculate our offset in the file */
971         group = index / mdt->entries_per_group;
972         group_offset = index % mdt->entries_per_group;
973         desc_block = group / mdt->groups_per_desc_block;
974         desc_offset = group % mdt->groups_per_desc_block;
975         blocknr_in_group = group_offset / mdt->entries_per_block;
976
977         /* To descgroup offset */
978         blknr = 1 + desc_block * mdt->blocks_per_desc_block;
979
980         /* To group offset */
981         blknr += desc_offset * mdt->blocks_per_group;
982
983         /* To actual file block */
984         blknr += 1 + blocknr_in_group;
985
986         *blocknr        = blknr;
987         *entry_in_block = group_offset % mdt->entries_per_block;
988 }
989
990 static int
991 ioread(struct open_file *f, off_t pos, void *buf, u_int length)
992 {
993         void *buffer;
994         int err;
995         int bsize = ((struct nandfs *)f->f_fsdata)->nf_sectorsize;
996         u_int off, nsec;
997
998         off = pos % bsize;
999         pos /= bsize;
1000         nsec = (length + (bsize - 1)) / bsize;
1001
1002         NANDFS_DEBUG("pos=%lld length=%d off=%d nsec=%d\n", pos, length,
1003             off, nsec);
1004
1005         buffer = malloc(nsec * bsize);
1006
1007         err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, pos,
1008             nsec * bsize, buffer, NULL);
1009
1010         memcpy(buf, buffer + off, length);
1011         free(buffer);
1012
1013         return (err);
1014 }
1015
1016 static int
1017 nandfs_probe_sectorsize(struct open_file *f)
1018 {
1019         void *buffer;
1020         int i, err;
1021
1022         buffer = malloc(16 * 1024);
1023
1024         NANDFS_DEBUG("probing for sector size: ");
1025
1026         for (i = 512; i < (16 * 1024); i <<= 1) {
1027                 NANDFS_DEBUG("%d ", i);
1028                 err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 0, i,
1029                     buffer, NULL);
1030
1031                 if (err == 0) {
1032                         NANDFS_DEBUG("found");
1033                         free(buffer);
1034                         return (i);
1035                 }
1036         }
1037
1038         free(buffer);
1039         NANDFS_DEBUG("not found\n");
1040         return (-1);
1041 }