2 * Copyright (c) 2011-2013 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@dragonflybsd.org>
6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
18 * 3. Neither the name of The DragonFly Project nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific, prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #if !defined(BOOT2) && !defined(TESTING)
45 #include <sys/types.h>
63 #include <machine/param.h> /* for DEV_BSHIFT */
64 #include <vfs/hammer2/hammer2_disk.h>
66 uint32_t iscsi_crc32(const void *buf, size_t size);
67 uint32_t iscsi_crc32_ext(const void *buf, size_t size, uint32_t ocrc);
69 static hammer2_media_data_t media;
70 static hammer2_blockref_t saved_base;
72 #define hammer2_icrc32(buf, size) iscsi_crc32(buf, size)
75 hammer2_blockref_t sroot;
76 hammer2_blockset_t sroot_blockset;
79 #elif defined(LIBSTAND)
82 /* BOOT2 doesn't use a descriptor */
84 #error "hammer2: unknown library API"
88 struct hammer2_inode {
89 struct hammer2_inode_data ino; /* raw inode data */
90 off_t doff; /* disk inode offset */
96 bzero(void *buf, size_t size)
98 for (size_t i = 0; i < size; i++)
103 bcopy(void *src, void *dst, size_t size)
105 memcpy(dst, src, size);
110 strlen(const char *s)
120 memcmp(const void *a, const void *b, size_t len)
122 for (size_t p = 0; p < len; p++) {
123 int r = ((const char *)a)[p] - ((const char *)b)[p];
135 blockoff(hammer2_blockref_t *bref)
137 return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX);
142 blocksize(hammer2_blockref_t *bref)
146 bytes = (size_t)(bref->data_off & HAMMER2_OFF_MASK_RADIX);
148 bytes = (size_t)1 << bytes;
154 hammer2_dirhash(const unsigned char *name, size_t len)
156 const unsigned char *aname = name;
168 for (i = j = 0; i < len; ++i) {
169 if (aname[i] == '.' ||
174 crcx += hammer2_icrc32(aname + j, i - j);
179 crcx += hammer2_icrc32(aname + j, i - j);
182 * The directory hash utilizes the top 32 bits of the 64-bit key.
183 * Bit 63 must be set to 1.
186 key |= (uint64_t)crcx << 32;
189 * l16 - crc of entire filename
191 * This crc reduces degenerate hash collision conditions
193 crcx = hammer2_icrc32(aname, len);
194 crcx = crcx ^ (crcx << 16);
195 key |= crcx & 0xFFFF0000U;
198 * Set bit 15. This allows readdir to strip bit 63 so a positive
199 * 64-bit cookie/offset can always be returned, and still guarantee
200 * that the values 0x0000-0x7FFF are available for artificial entries.
213 h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off)
215 #if defined(LIBSTAND)
221 rc = pread(hfs->fd, &media, nbytes, off);
222 if (rc == (int)nbytes)
226 #elif defined(LIBSTAND)
227 rc = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ,
228 off >> DEV_BSHIFT, nbytes,
230 if (rc || rlen != nbytes)
233 /* BIOS interface may barf on 64KB reads */
235 while (nbytes > 16384) {
236 rc = dskread(buf, off >> DEV_BSHIFT, 16384 >> DEV_BSHIFT);
238 buf = (char *)buf + 16384;
242 rc = dskread(buf, off >> DEV_BSHIFT, nbytes >> DEV_BSHIFT);
246 #error "hammer2: unknown library API"
254 * Initialize for HAMMER2 filesystem access given a hammer2_fs with
255 * its device file descriptor initialized.
259 * Lookup within the block specified by (*base), loading the block from disk
260 * if necessary. Locate the first key within the requested range and
261 * recursively run through indirect blocks as needed. The caller may loop
262 * by setting key_beg to *key_ret.
264 * Returns 0 if nothing could be found and the key range has been exhausted.
265 * returns -1 if a disk error occured. Otherwise returns the size of the
266 * data block and returns the data block in *pptr and bref in *bref_ret.
268 * NOTE! When reading file data, the returned bref's key will be the nearest
269 * data block we looked up. The file read procedure must handle any
270 * zero-fill or skip. However, we will truncate the return value to
274 h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base,
275 hammer2_key_t key_beg, hammer2_key_t key_end,
276 hammer2_blockref_t *bref_ret, void **pptr)
278 hammer2_blockref_t *bref;
279 hammer2_blockref_t best;
280 hammer2_key_t scan_beg;
281 hammer2_key_t scan_end;
289 saved_base.data_off = (hammer2_off_t)-1;
292 if (base->data_off == (hammer2_off_t)-1) {
298 * Calculate the number of blockrefs to scan
301 case HAMMER2_BREF_TYPE_VOLUME:
302 count = HAMMER2_SET_COUNT;
304 case HAMMER2_BREF_TYPE_INODE:
305 count = HAMMER2_SET_COUNT;
307 case HAMMER2_BREF_TYPE_INDIRECT:
308 count = blocksize(base) / sizeof(hammer2_blockref_t);
316 * Find the best candidate (the lowest blockref within the specified
317 * range). The array can be fully set associative so make no ordering
321 best.key = HAMMER2_KEY_MAX;
324 for (i = 0; i < count; ++i) {
326 * [re]load when returning from our recursion
328 if (base->type != HAMMER2_BREF_TYPE_VOLUME &&
329 base->data_off != saved_base.data_off) {
330 if (blocksize(base) && h2read(hfs, &media,
340 * Special case embedded file data
342 if (base->type == HAMMER2_BREF_TYPE_INODE) {
343 if (media.ipdata.meta.op_flags &
344 HAMMER2_OPFLAG_DIRECTDATA) {
345 *pptr = media.ipdata.u.data;
346 bref_ret->type = HAMMER2_BREF_TYPE_DATA;
348 return HAMMER2_EMBEDDED_BYTES;
353 * Calculate the bref in our scan.
356 case HAMMER2_BREF_TYPE_VOLUME:
357 bref = &hfs->sroot_blockset.blockref[i];
359 case HAMMER2_BREF_TYPE_INODE:
360 bref = &media.ipdata.u.blockset.blockref[i];
362 case HAMMER2_BREF_TYPE_INDIRECT:
363 bref = &media.npdata[i];
368 if (bref->key > best.key)
370 scan_beg = bref->key;
371 scan_end = scan_beg + ((hammer2_key_t)1 << bref->keybits) - 1;
372 if (scan_end >= key_beg && scan_beg <= key_end) {
378 * Figure out what to do with the results.
388 case HAMMER2_BREF_TYPE_INDIRECT:
390 * Matched an indirect block. If the block turns out to
391 * contain nothing we continue the iteration, otherwise
392 * we return the data from the recursion.
394 * Be sure to handle the overflow case when recalculating
397 rc = h2lookup(hfs, &best, key_beg, key_end, bref_ret, pptr);
400 ((hammer2_key_t)1 << best.keybits);
401 if (key_beg > best.key && key_beg <= key_end)
405 case HAMMER2_BREF_TYPE_DIRENT:
406 case HAMMER2_BREF_TYPE_INODE:
407 case HAMMER2_BREF_TYPE_DATA:
409 * Terminal match. Leaf elements might not be data-aligned.
411 dev_bsize = blocksize(&best);
413 if (dev_bsize < HAMMER2_LBUFSIZE)
414 dev_bsize = HAMMER2_LBUFSIZE;
415 dev_boff = blockoff(&best) -
416 (blockoff(&best) & ~HAMMER2_LBUFMASK64);
417 if (h2read(hfs, &media,
419 blockoff(&best) - dev_boff)) {
423 saved_base.data_off = (hammer2_off_t)-1;
425 *pptr = media.buf + dev_boff;
426 rc = blocksize(&best);
434 h2resolve(struct hammer2_fs *hfs, const char *path,
435 hammer2_blockref_t *bref, hammer2_inode_data_t **inop)
437 hammer2_blockref_t bres;
438 hammer2_inode_data_t *ino;
445 * Start point (superroot)
453 * Iterate path elements
458 if (*path == 0) /* terminal */
462 * Calculate path element and look for it in the directory
464 for (len = 0; path[len]; ++len) {
465 if (path[len] == '/')
468 key = hammer2_dirhash(path, len);
470 bytes = h2lookup(hfs, bref,
472 &bres, (void **)&data);
478 case HAMMER2_BREF_TYPE_DIRENT:
479 if (bres.embed.dirent.namlen != len)
481 if (bres.embed.dirent.namlen <=
482 sizeof(bres.check.buf)) {
483 if (memcmp(path, bres.check.buf, len))
486 if (memcmp(path, data, len))
491 * Found, resolve inode. This will set
492 * ino similarly to HAMMER2_BREF_TYPE_INODE
493 * and adjust bres, which path continuation
497 bytes = h2lookup(hfs, bref,
498 bres.embed.dirent.inum,
499 bres.embed.dirent.inum,
500 &bres, (void **)&ino);
504 break; /* NOT REACHED */
505 case HAMMER2_BREF_TYPE_INODE:
507 if (ino->meta.name_len != len)
509 if (memcmp(path, ino->filename, len) == 0) {
516 if ((bres.key & 0xFFFF) == 0xFFFF) {
527 if (bytes < 0 || bres.type == 0) {
528 bref->data_off = (hammer2_off_t)-1;
534 * Check path continuance, inode must be a directory or
538 if (*path && ino->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
539 bref->data_off = (hammer2_off_t)-1;
548 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref,
549 off_t off, off_t filesize, void *buf, size_t len)
551 hammer2_blockref_t bres;
562 if (off + len > filesize)
563 len = filesize - off;
571 * Find closest bres >= requested offset.
573 bytes = h2lookup(hfs, bref, off, off + len - 1,
574 &bres, (void **)&data);
583 * Load the data into the buffer. First handle a degenerate
593 * Returned record overlaps to the left of the requested
594 * position. It must overlap in this case or h2lookup()
595 * would have returned something else.
597 if (bres.key < off) {
598 data += off - bres.key;
599 bytes -= off - bres.key;
603 * Returned record overlaps to the right of the requested
604 * position, handle zero-fill. Again h2lookup() only returns
605 * this case if there is an actual overlap.
607 if (bres.key > off) {
608 zfill = (ssize_t)(bres.key - off);
613 buf = (char *)buf + zfill;
617 * Trim returned request before copying.
621 bcopy(data, buf, bytes);
625 buf = (char *)buf + bytes;
632 h2init(struct hammer2_fs *hfs)
637 hammer2_tid_t best_tid = 0;
645 * Find the best volume header.
647 * WARNING BIOS BUGS: It looks like some BIOSes will implode when
648 * given a disk offset beyond the EOM. XXX We need to probe the
649 * size of the media and limit our accesses, until then we have
650 * to give up if the first volume header does not have a hammer2
653 * XXX Probably still going to be problems w/ HAMMER2 volumes on
654 * media which is too small w/certain BIOSes.
657 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
658 off = i * HAMMER2_ZONE_BYTES64;
661 if (h2read(hfs, &media, sizeof(media.voldata), off))
663 if (media.voldata.magic != HAMMER2_VOLUME_ID_HBO)
665 if (best < 0 || best_tid < media.voldata.mirror_tid) {
667 best_tid = media.voldata.mirror_tid;
675 * Reload the best volume header and set up the blockref.
676 * We messed with media, clear the cache before continuing.
678 off = best * HAMMER2_ZONE_BYTES64;
679 if (h2read(hfs, &media, sizeof(media.voldata), off))
681 hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME;
682 hfs->sroot.data_off = off;
683 hfs->sroot_blockset = media.voldata.sroot_blockset;
684 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
687 * Lookup sroot/BOOT and clear the cache again.
689 r = h2lookup(hfs, &hfs->sroot,
690 HAMMER2_SROOT_KEY, HAMMER2_SROOT_KEY,
694 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
695 r = h2lookup(hfs, &hfs->sroot,
696 HAMMER2_BOOT_KEY, HAMMER2_BOOT_KEY,
699 printf("hammer2: 'BOOT' PFS not found\n");
702 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
707 /************************************************************************
709 ************************************************************************
714 static struct hammer2_fs hfs;
717 boot2_hammer2_init(void)
725 boot2_hammer2_lookup(const char *path)
727 hammer2_blockref_t bref;
729 h2resolve(&hfs, path, &bref, NULL);
730 return ((boot2_ino_t)bref.data_off);
734 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len)
736 hammer2_blockref_t bref;
739 bzero(&bref, sizeof(bref));
740 bref.type = HAMMER2_BREF_TYPE_INODE;
743 total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len);
749 const struct boot2_fsapi boot2_hammer2_api = {
750 .fsinit = boot2_hammer2_init,
751 .fslookup = boot2_hammer2_lookup,
752 .fsread = boot2_hammer2_read
757 /************************************************************************
759 ************************************************************************
765 struct hammer2_fs hfs;
766 hammer2_blockref_t bref;
774 hammer2_get_dtype(uint8_t type)
777 case HAMMER2_OBJTYPE_DIRECTORY:
779 case HAMMER2_OBJTYPE_REGFILE:
781 case HAMMER2_OBJTYPE_FIFO:
783 case HAMMER2_OBJTYPE_CDEV:
785 case HAMMER2_OBJTYPE_BDEV:
787 case HAMMER2_OBJTYPE_SOFTLINK:
789 case HAMMER2_OBJTYPE_SOCKET:
798 hammer2_get_mode(uint8_t type)
801 case HAMMER2_OBJTYPE_DIRECTORY:
803 case HAMMER2_OBJTYPE_REGFILE:
805 case HAMMER2_OBJTYPE_FIFO:
807 case HAMMER2_OBJTYPE_CDEV:
809 case HAMMER2_OBJTYPE_BDEV:
811 case HAMMER2_OBJTYPE_SOFTLINK:
813 case HAMMER2_OBJTYPE_SOCKET:
821 hammer2_open(const char *path, struct open_file *f)
823 struct hfile *hf = malloc(sizeof(*hf));
824 hammer2_inode_data_t *ipdata;
826 bzero(hf, sizeof(*hf));
831 if (h2init(&hf->hfs)) {
837 h2resolve(&hf->hfs, path, &hf->bref, &ipdata);
838 if (hf->bref.data_off == (hammer2_off_t)-1 ||
839 (hf->bref.type != HAMMER2_BREF_TYPE_INODE &&
840 hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) {
847 hf->fsize = ipdata->meta.size;
848 hf->type = ipdata->meta.type;
849 hf->mode = ipdata->meta.mode |
850 hammer2_get_mode(ipdata->meta.type);
853 hf->type = HAMMER2_OBJTYPE_DIRECTORY;
854 hf->mode = 0755 | S_IFDIR;
860 hammer2_close(struct open_file *f)
862 struct hfile *hf = f->f_fsdata;
871 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid)
873 struct hfile *hf = f->f_fsdata;
877 total = h2readfile(&hf->hfs, &hf->bref,
878 f->f_offset, hf->fsize, buf, len);
883 f->f_offset += total;
886 *resid = len - total;
891 hammer2_seek(struct open_file *f, off_t offset, int whence)
893 struct hfile *hf = f->f_fsdata;
897 f->f_offset = offset;
900 f->f_offset += offset;
903 f->f_offset = hf->fsize - offset;
908 return (f->f_offset);
912 hammer2_stat(struct open_file *f, struct stat *st)
914 struct hfile *hf = f->f_fsdata;
916 st->st_mode = hf->mode;
919 st->st_size = hf->fsize;
925 hammer2_readdir(struct open_file *f, struct dirent *den)
927 struct hfile *hf = f->f_fsdata;
928 hammer2_blockref_t bres;
929 hammer2_inode_data_t *ipdata;
934 bytes = h2lookup(&hf->hfs, &hf->bref,
935 f->f_offset | HAMMER2_DIRHASH_VISIBLE,
937 &bres, (void **)&data);
941 case HAMMER2_BREF_TYPE_INODE:
943 den->d_namlen = ipdata->meta.name_len;
944 den->d_type = hammer2_get_dtype(ipdata->meta.type);
945 den->d_ino = ipdata->meta.inum;
946 bcopy(ipdata->filename, den->d_name, den->d_namlen);
947 den->d_name[den->d_namlen] = 0;
949 case HAMMER2_BREF_TYPE_DIRENT:
950 den->d_namlen = bres.embed.dirent.namlen;
951 den->d_type = hammer2_get_dtype(bres.embed.dirent.type);
952 den->d_ino = bres.embed.dirent.inum;
953 if (den->d_namlen <= sizeof(bres.check.buf)) {
954 bcopy(bres.check.buf,
958 bcopy(data, den->d_name, den->d_namlen);
960 den->d_name[den->d_namlen] = 0;
965 hammer2_get_dtype(HAMMER2_OBJTYPE_REGFILE);
966 den->d_name[0] = '?';
971 f->f_offset = bres.key + 1;
978 struct fs_ops hammer_fsops = {