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 <vfs/hammer2/hammer2_disk.h>
65 uint32_t iscsi_crc32(const void *buf, size_t size);
66 uint32_t iscsi_crc32_ext(const void *buf, size_t size, uint32_t ocrc);
68 static hammer2_media_data_t media;
69 static hammer2_blockref_t saved_base;
71 #define hammer2_icrc32(buf, size) iscsi_crc32(buf, size)
74 hammer2_blockref_t sroot;
75 hammer2_blockset_t sroot_blockset;
78 #elif defined(LIBSTAND)
81 /* BOOT2 doesn't use a descriptor */
83 #error "hammer2: unknown library API"
87 struct hammer2_inode {
88 struct hammer2_inode_data ino; /* raw inode data */
89 off_t doff; /* disk inode offset */
95 bzero(void *buf, size_t size)
97 for (size_t i = 0; i < size; i++)
102 bcopy(void *src, void *dst, size_t size)
104 memcpy(dst, src, size);
109 strlen(const char *s)
119 memcmp(const void *a, const void *b, size_t len)
121 for (size_t p = 0; p < len; p++) {
122 int r = ((const char *)a)[p] - ((const char *)b)[p];
134 blockoff(hammer2_blockref_t *bref)
136 return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX);
141 blocksize(hammer2_blockref_t *bref)
143 return(1 << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX));
148 hammer2_dirhash(const unsigned char *name, size_t len)
150 const unsigned char *aname = name;
162 for (i = j = 0; i < len; ++i) {
163 if (aname[i] == '.' ||
168 crcx += hammer2_icrc32(aname + j, i - j);
173 crcx += hammer2_icrc32(aname + j, i - j);
176 * The directory hash utilizes the top 32 bits of the 64-bit key.
177 * Bit 63 must be set to 1.
180 key |= (uint64_t)crcx << 32;
183 * l16 - crc of entire filename
185 * This crc reduces degenerate hash collision conditions
187 crcx = hammer2_icrc32(aname, len);
188 crcx = crcx ^ (crcx << 16);
189 key |= crcx & 0xFFFF0000U;
192 * Set bit 15. This allows readdir to strip bit 63 so a positive
193 * 64-bit cookie/offset can always be returned, and still guarantee
194 * that the values 0x0000-0x7FFF are available for artificial entries.
207 h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off)
209 #if defined(LIBSTAND)
215 rc = pread(hfs->fd, &media, nbytes, off);
216 if (rc == (int)nbytes)
220 #elif defined(LIBSTAND)
221 rc = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ,
222 off >> DEV_BSHIFT, nbytes,
224 if (rc || rlen != nbytes)
227 rc = dskread(buf, off >> DEV_BSHIFT, nbytes >> DEV_BSHIFT);
231 #error "hammer2: unknown library API"
239 * Initialize for HAMMER2 filesystem access given a hammer2_fs with
240 * its device file descriptor initialized.
244 * Lookup within the block specified by (*base), loading the block from disk
245 * if necessary. Locate the first key within the requested range and
246 * recursively run through indirect blocks as needed. The caller may loop
247 * by setting key_beg to *key_ret.
249 * Returns 0 if nothing could be found and the key range has been exhausted.
250 * returns -1 if a disk error occured. Otherwise returns the size of the
251 * data block and returns the data block in *pptr and bref in *bref_ret.
253 * NOTE! When reading file data, the returned bref's key will be the nearest
254 * data block we looked up. The file read procedure must handle any
255 * zero-fill or skip. However, we will truncate the return value to
259 h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base,
260 hammer2_key_t key_beg, hammer2_key_t key_end,
261 hammer2_blockref_t *bref_ret, void **pptr)
263 hammer2_blockref_t *bref;
264 hammer2_blockref_t best;
265 hammer2_key_t scan_beg;
266 hammer2_key_t scan_end;
274 saved_base.data_off = (hammer2_off_t)-1;
277 if (base->data_off == (hammer2_off_t)-1)
281 * Calculate the number of blockrefs to scan
284 case HAMMER2_BREF_TYPE_VOLUME:
285 count = HAMMER2_SET_COUNT;
287 case HAMMER2_BREF_TYPE_INODE:
288 count = HAMMER2_SET_COUNT;
290 case HAMMER2_BREF_TYPE_INDIRECT:
291 count = blocksize(base) / sizeof(hammer2_blockref_t);
296 * Find the best candidate (the lowest blockref within the specified
297 * range). The array can be fully set associative so make no ordering
301 best.key = HAMMER2_KEY_MAX;
304 for (i = 0; i < count; ++i) {
306 * [re]load when returning from our recursion
308 if (base->type != HAMMER2_BREF_TYPE_VOLUME &&
309 base->data_off != saved_base.data_off) {
310 if (h2read(hfs, &media,
311 blocksize(base), blockoff(base))) {
318 * Special case embedded file data
320 if (base->type == HAMMER2_BREF_TYPE_INODE) {
321 if (media.ipdata.op_flags & HAMMER2_OPFLAG_DIRECTDATA) {
322 *pptr = media.ipdata.u.data;
323 bref_ret->type = HAMMER2_BREF_TYPE_DATA;
325 return HAMMER2_EMBEDDED_BYTES;
330 * Calculate the bref in our scan.
333 case HAMMER2_BREF_TYPE_VOLUME:
334 bref = &hfs->sroot_blockset.blockref[i];
336 case HAMMER2_BREF_TYPE_INODE:
337 bref = &media.ipdata.u.blockset.blockref[i];
339 case HAMMER2_BREF_TYPE_INDIRECT:
340 bref = &media.npdata[i];
345 if (bref->key > best.key)
347 scan_beg = bref->key;
348 scan_end = scan_beg + ((hammer2_key_t)1 << bref->keybits) - 1;
349 if (scan_end >= key_beg && scan_beg <= key_end) {
355 * Figure out what to do with the results.
364 case HAMMER2_BREF_TYPE_INDIRECT:
366 * Matched an indirect block. If the block turns out to
367 * contain nothing we continue the iteration, otherwise
368 * we return the data from the recursion.
370 * Be sure to handle the overflow case when recalculating
373 rc = h2lookup(hfs, &best, key_beg, key_end, bref_ret, pptr);
376 ((hammer2_key_t)1 << best.keybits);
377 if (key_beg > best.key && key_beg <= key_end)
381 case HAMMER2_BREF_TYPE_INODE:
382 case HAMMER2_BREF_TYPE_DATA:
384 * Terminal match. Leaf elements might not be data-aligned.
386 dev_bsize = blocksize(&best);
387 if (dev_bsize < HAMMER2_LBUFSIZE)
388 dev_bsize = HAMMER2_LBUFSIZE;
389 dev_boff = blockoff(&best) -
390 (blockoff(&best) & ~HAMMER2_LBUFMASK64);
391 if (h2read(hfs, &media, dev_bsize, blockoff(&best) - dev_boff))
393 saved_base.data_off = (hammer2_off_t)-1;
395 *pptr = media.buf + dev_boff;
396 rc = blocksize(&best);
404 h2resolve(struct hammer2_fs *hfs, const char *path,
405 hammer2_blockref_t *bref, hammer2_inode_data_t **inop)
407 hammer2_blockref_t bres;
408 hammer2_inode_data_t *ino;
414 * Start point (superroot)
421 * Iterate path elements
426 if (*path == 0) /* terminal */
430 * Calculate path element and look for it in the directory
432 for (len = 0; path[len]; ++len) {
433 if (path[len] == '/')
436 key = hammer2_dirhash(path, len);
438 bytes = h2lookup(hfs, bref,
440 &bres, (void **)&ino);
443 if (len == ino->name_len &&
444 memcmp(path, ino->filename, len) == 0) {
456 bref->data_off = (hammer2_off_t)-1;
461 * Check path continuance, inode must be a directory or
465 if (*path && ino->type != HAMMER2_OBJTYPE_DIRECTORY) {
466 bref->data_off = (hammer2_off_t)-1;
475 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref,
476 off_t off, off_t filesize, void *buf, size_t len)
478 hammer2_blockref_t bres;
489 if (off + len > filesize)
490 len = filesize - off;
498 * Find closest bres >= requested offset.
500 bytes = h2lookup(hfs, bref, off, off + len - 1,
501 &bres, (void **)&data);
510 * Load the data into the buffer. First handle a degenerate
520 * Returned record overlaps to the left of the requested
521 * position. It must overlap in this case or h2lookup()
522 * would have returned something else.
524 if (bres.key < off) {
525 data += off - bres.key;
526 bytes -= off - bres.key;
530 * Returned record overlaps to the right of the requested
531 * position, handle zero-fill. Again h2lookup() only returns
532 * this case if there is an actual overlap.
534 if (bres.key > off) {
535 zfill = (ssize_t)(bres.key - off);
540 buf = (char *)buf + zfill;
544 * Trim returned request before copying.
548 bcopy(data, buf, bytes);
552 buf = (char *)buf + bytes;
559 h2init(struct hammer2_fs *hfs)
564 hammer2_tid_t best_tid = 0;
571 * Find the best volume header.
573 * WARNING BIOS BUGS: It looks like some BIOSes will implode when
574 * given a disk offset beyond the EOM. XXX We need to probe the
575 * size of the media and limit our accesses, until then we have
576 * to give up if the first volume header does not have a hammer2
579 * XXX Probably still going to be problems w/ HAMMER2 volumes on
580 * media which is too small w/certain BIOSes.
583 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
584 off = i * HAMMER2_ZONE_BYTES64;
587 if (h2read(hfs, &media, sizeof(media.voldata), off))
589 if (media.voldata.magic != HAMMER2_VOLUME_ID_HBO)
591 if (best < 0 || best_tid < media.voldata.mirror_tid) {
593 best_tid = media.voldata.mirror_tid;
601 * Reload the best volume header and set up the blockref.
602 * We messed with media, clear the cache before continuing.
604 off = best * HAMMER2_ZONE_BYTES64;
605 if (h2read(hfs, &media, sizeof(media.voldata), off))
607 hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME;
608 hfs->sroot.data_off = off;
609 hfs->sroot_blockset = media.voldata.sroot_blockset;
610 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
613 * Lookup sroot and clear the cache again.
615 h2lookup(hfs, &hfs->sroot, 0, 0, &hfs->sroot, &data);
616 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
621 /************************************************************************
623 ************************************************************************
628 static struct hammer2_fs hfs;
631 boot2_hammer2_init(void)
639 boot2_hammer2_lookup(const char *path)
641 hammer2_blockref_t bref;
643 h2resolve(&hfs, path, &bref, NULL);
644 return ((boot2_ino_t)bref.data_off);
648 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len)
650 hammer2_blockref_t bref;
653 bzero(&bref, sizeof(bref));
654 bref.type = HAMMER2_BREF_TYPE_INODE;
657 total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len);
663 const struct boot2_fsapi boot2_hammer2_api = {
664 .fsinit = boot2_hammer2_init,
665 .fslookup = boot2_hammer2_lookup,
666 .fsread = boot2_hammer2_read
671 /************************************************************************
673 ************************************************************************
679 struct hammer2_fs hfs;
680 hammer2_blockref_t bref;
688 hammer2_get_dtype(uint8_t type)
691 case HAMMER2_OBJTYPE_DIRECTORY:
693 case HAMMER2_OBJTYPE_REGFILE:
695 case HAMMER2_OBJTYPE_FIFO:
697 case HAMMER2_OBJTYPE_CDEV:
699 case HAMMER2_OBJTYPE_BDEV:
701 case HAMMER2_OBJTYPE_SOFTLINK:
703 case HAMMER2_OBJTYPE_HARDLINK:
705 case HAMMER2_OBJTYPE_SOCKET:
714 hammer2_get_mode(uint8_t type)
717 case HAMMER2_OBJTYPE_DIRECTORY:
719 case HAMMER2_OBJTYPE_REGFILE:
721 case HAMMER2_OBJTYPE_FIFO:
723 case HAMMER2_OBJTYPE_CDEV:
725 case HAMMER2_OBJTYPE_BDEV:
727 case HAMMER2_OBJTYPE_SOFTLINK:
729 case HAMMER2_OBJTYPE_HARDLINK:
731 case HAMMER2_OBJTYPE_SOCKET:
739 hammer2_open(const char *path, struct open_file *f)
741 struct hfile *hf = malloc(sizeof(*hf));
742 hammer2_inode_data_t *ipdata;
744 bzero(hf, sizeof(*hf));
749 if (h2init(&hf->hfs)) {
755 h2resolve(&hf->hfs, path, &hf->bref, &ipdata);
756 if (hf->bref.data_off == (hammer2_off_t)-1 ||
757 (hf->bref.type != HAMMER2_BREF_TYPE_INODE &&
758 hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) {
765 hf->fsize = ipdata->size;
766 hf->type = ipdata->type;
767 hf->mode = ipdata->mode | hammer2_get_mode(ipdata->type);
770 hf->type = HAMMER2_OBJTYPE_DIRECTORY;
771 hf->mode = 0755 | S_IFDIR;
777 hammer2_close(struct open_file *f)
779 struct hfile *hf = f->f_fsdata;
788 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid)
790 struct hfile *hf = f->f_fsdata;
794 total = h2readfile(&hf->hfs, &hf->bref,
795 f->f_offset, hf->fsize, buf, len);
800 f->f_offset += total;
803 *resid = len - total;
808 hammer2_seek(struct open_file *f, off_t offset, int whence)
810 struct hfile *hf = f->f_fsdata;
814 f->f_offset = offset;
817 f->f_offset += offset;
820 f->f_offset = hf->fsize - offset;
825 return (f->f_offset);
829 hammer2_stat(struct open_file *f, struct stat *st)
831 struct hfile *hf = f->f_fsdata;
833 st->st_mode = hf->mode;
836 st->st_size = hf->fsize;
842 hammer2_readdir(struct open_file *f, struct dirent *den)
844 struct hfile *hf = f->f_fsdata;
845 hammer2_blockref_t bres;
846 hammer2_inode_data_t *ipdata;
850 bytes = h2lookup(&hf->hfs, &hf->bref,
851 f->f_offset | HAMMER2_DIRHASH_VISIBLE,
853 &bres, (void **)&ipdata);
856 den->d_namlen = ipdata->name_len;
857 den->d_type = hammer2_get_dtype(ipdata->type);
858 den->d_ino = ipdata->inum;
859 bcopy(ipdata->filename, den->d_name, den->d_namlen);
860 den->d_name[den->d_namlen] = 0;
862 f->f_offset = bres.key + 1;
869 struct fs_ops hammer_fsops = {