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