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 #define hammer2_icrc32(buf, size) iscsi_crc32(buf, size)
71 struct hammer2_volume_data vol;
72 hammer2_blockref_t sroot;
75 #elif defined(LIBSTAND)
78 /* BOOT2 doesn't use a descriptor */
80 #error "hammer2: unknown library API"
84 struct hammer2_inode {
85 struct hammer2_inode_data ino; /* raw inode data */
86 off_t doff; /* disk inode offset */
92 bzero(void *buf, size_t size)
94 for (size_t i = 0; i < size; i++)
99 bcopy(void *src, void *dst, size_t size)
101 memcpy(dst, src, size);
106 strlen(const char *s)
116 memcmp(const void *a, const void *b, size_t len)
118 for (size_t p = 0; p < len; p++) {
119 int r = ((const char *)a)[p] - ((const char *)b)[p];
131 blockoff(hammer2_blockref_t *bref)
133 return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX);
138 blocksize(hammer2_blockref_t *bref)
140 return(1 << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX));
145 hammer2_dirhash(const unsigned char *name, size_t len)
147 const unsigned char *aname = name;
159 for (i = j = 0; i < len; ++i) {
160 if (aname[i] == '.' ||
165 crcx += hammer2_icrc32(aname + j, i - j);
170 crcx += hammer2_icrc32(aname + j, i - j);
173 * The directory hash utilizes the top 32 bits of the 64-bit key.
174 * Bit 63 must be set to 1.
177 key |= (uint64_t)crcx << 32;
180 * l16 - crc of entire filename
182 * This crc reduces degenerate hash collision conditions
184 crcx = hammer2_icrc32(aname, len);
185 crcx = crcx ^ (crcx << 16);
186 key |= crcx & 0xFFFF0000U;
189 * Set bit 15. This allows readdir to strip bit 63 so a positive
190 * 64-bit cookie/offset can always be returned, and still guarantee
191 * that the values 0x0000-0x7FFF are available for artificial entries.
204 h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off)
206 #if defined(LIBSTAND)
212 rc = pread(hfs->fd, &hfs->vol, nbytes, off);
213 if (rc == (int)nbytes)
217 #elif defined(LIBSTAND)
218 rc = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ,
219 off >> DEV_BSHIFT, nbytes,
221 if (rc || rlen != nbytes)
224 rc = dskread(buf, off >> DEV_BSHIFT, nbytes >> DEV_BSHIFT);
228 #error "hammer2: unknown library API"
236 * Initialize for HAMMER2 filesystem access given a hammer2_fs with
237 * its device file descriptor initialized.
241 * Lookup within the block specified by (*base), loading the block from disk
242 * if necessary. Locate the first key within the requested range and
243 * recursively run through indirect blocks as needed. The caller may loop
244 * by setting key_beg to *key_ret.
246 * Returns 0 if nothing could be found and the key range has been exhausted.
247 * returns -1 if a disk error occured. Otherwise returns the size of the
248 * data block and returns the data block in *pptr and bref in *bref_ret.
250 * NOTE! When reading file data, the returned bref's key will be the nearest
251 * data block we looked up. The file read procedure must handle any
252 * zero-fill or skip. However, we will truncate the return value to
256 h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base,
257 hammer2_key_t key_beg, hammer2_key_t key_end,
258 hammer2_blockref_t *bref_ret, void **pptr)
260 hammer2_blockref_t *bref;
261 hammer2_blockref_t best;
262 hammer2_key_t scan_beg;
263 hammer2_key_t scan_end;
269 static hammer2_media_data_t media;
270 static hammer2_blockref_t saved_base;
273 saved_base.data_off = (hammer2_off_t)-1;
276 if (base->data_off == (hammer2_off_t)-1)
280 * Calculate the number of blockrefs to scan
283 case HAMMER2_BREF_TYPE_VOLUME:
284 count = HAMMER2_SET_COUNT;
286 case HAMMER2_BREF_TYPE_INODE:
287 count = HAMMER2_SET_COUNT;
289 case HAMMER2_BREF_TYPE_INDIRECT:
290 count = blocksize(base) / sizeof(hammer2_blockref_t);
295 * Find the best candidate (the lowest blockref within the specified
296 * range). The array can be fully set associative so make no ordering
300 best.key = HAMMER2_KEY_MAX;
303 for (i = 0; i < count; ++i) {
305 * [re]load when returning from our recursion
307 if (base->type != HAMMER2_BREF_TYPE_VOLUME &&
308 base->data_off != saved_base.data_off) {
309 if (h2read(hfs, &media,
310 blocksize(base), blockoff(base))) {
317 * Special case embedded file data
319 if (base->type == HAMMER2_BREF_TYPE_INODE) {
320 if (media.ipdata.op_flags & HAMMER2_OPFLAG_DIRECTDATA) {
321 *pptr = media.ipdata.u.data;
322 bref_ret->type = HAMMER2_BREF_TYPE_DATA;
324 return HAMMER2_EMBEDDED_BYTES;
329 * Calculate the bref in our scan.
332 case HAMMER2_BREF_TYPE_VOLUME:
333 bref = &hfs->vol.sroot_blockset.blockref[i];
335 case HAMMER2_BREF_TYPE_INODE:
336 bref = &media.ipdata.u.blockset.blockref[i];
338 case HAMMER2_BREF_TYPE_INDIRECT:
339 bref = &media.npdata[i];
344 if (bref->key > best.key)
346 scan_beg = bref->key;
347 scan_end = scan_beg + ((hammer2_key_t)1 << bref->keybits) - 1;
348 if (scan_end >= key_beg && scan_beg <= key_end) {
354 * Figure out what to do with the results.
363 case HAMMER2_BREF_TYPE_INDIRECT:
365 * Matched an indirect block. If the block turns out to
366 * contain nothing we continue the iteration, otherwise
367 * we return the data from the recursion.
369 * Be sure to handle the overflow case when recalculating
372 rc = h2lookup(hfs, &best, key_beg, key_end, bref_ret, pptr);
375 ((hammer2_key_t)1 << best.keybits);
376 if (key_beg > best.key && key_beg <= key_end)
380 case HAMMER2_BREF_TYPE_INODE:
381 case HAMMER2_BREF_TYPE_DATA:
383 * Terminal match. Leaf elements might not be data-aligned.
385 dev_bsize = blocksize(&best);
386 if (dev_bsize < HAMMER2_LBUFSIZE)
387 dev_bsize = HAMMER2_LBUFSIZE;
388 dev_boff = blockoff(&best) -
389 (blockoff(&best) & ~HAMMER2_LBUFMASK64);
390 if (h2read(hfs, &media, dev_bsize, blockoff(&best) - dev_boff))
392 saved_base.data_off = (hammer2_off_t)-1;
394 *pptr = media.buf + dev_boff;
395 rc = blocksize(&best);
403 h2resolve(struct hammer2_fs *hfs, const char *path,
404 hammer2_blockref_t *bref, hammer2_inode_data_t **inop)
406 hammer2_blockref_t bres;
407 hammer2_inode_data_t *ino;
413 * Start point (superroot)
420 * Iterate path elements
425 if (*path == 0) /* terminal */
429 * Calculate path element and look for it in the directory
431 for (len = 0; path[len]; ++len) {
432 if (path[len] == '/')
435 key = hammer2_dirhash(path, len);
437 bytes = h2lookup(hfs, bref,
439 &bres, (void **)&ino);
442 if (len == ino->name_len &&
443 memcmp(path, ino->filename, len) == 0) {
455 bref->data_off = (hammer2_off_t)-1;
460 * Check path continuance, inode must be a directory or
464 if (*path && ino->type != HAMMER2_OBJTYPE_DIRECTORY) {
465 bref->data_off = (hammer2_off_t)-1;
474 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref,
475 off_t off, off_t filesize, void *buf, size_t len)
477 hammer2_blockref_t bres;
488 if (off + len > filesize)
489 len = filesize - off;
497 * Find closest bres >= requested offset.
499 bytes = h2lookup(hfs, bref, off, off + len - 1,
500 &bres, (void **)&data);
509 * Load the data into the buffer. First handle a degenerate
519 * Returned record overlaps to the left of the requested
520 * position. It must overlap in this case or h2lookup()
521 * would have returned something else.
523 if (bres.key < off) {
524 data += off - bres.key;
525 bytes -= off - bres.key;
529 * Returned record overlaps to the right of the requested
530 * position, handle zero-fill. Again h2lookup() only returns
531 * this case if there is an actual overlap.
533 if (bres.key > off) {
534 zfill = (ssize_t)(bres.key - off);
539 buf = (char *)buf + zfill;
543 * Trim returned request before copying.
547 bcopy(data, buf, bytes);
551 buf = (char *)buf + bytes;
558 h2init(struct hammer2_fs *hfs)
563 hammer2_tid_t best_tid = 0;
570 * Find the best volume header
573 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
574 off = i * HAMMER2_ZONE_BYTES64;
577 if (h2read(hfs, &hfs->vol, sizeof(hfs->vol), off))
579 if (hfs->vol.magic != HAMMER2_VOLUME_ID_HBO)
581 if (best < 0 || best_tid < hfs->vol.mirror_tid) {
583 best_tid = hfs->vol.mirror_tid;
591 * Reload the best volume header and set up the blockref.
593 off = best * HAMMER2_ZONE_BYTES64;
594 if (h2read(hfs, &hfs->vol, sizeof(hfs->vol), off))
596 hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME;
597 hfs->sroot.data_off = off;
598 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
599 h2lookup(hfs, &hfs->sroot, 0, 0, &hfs->sroot, &data);
604 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
609 /************************************************************************
611 ************************************************************************
616 static struct hammer2_fs hfs;
619 boot2_hammer2_init(void)
627 boot2_hammer2_lookup(const char *path)
629 hammer2_blockref_t bref;
631 h2resolve(&hfs, path, &bref, NULL);
632 return ((boot2_ino_t)bref.data_off);
636 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len)
638 hammer2_blockref_t bref;
641 bzero(&bref, sizeof(bref));
642 bref.type = HAMMER2_BREF_TYPE_INODE;
645 total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len);
651 const struct boot2_fsapi boot2_hammer2_api = {
652 .fsinit = boot2_hammer2_init,
653 .fslookup = boot2_hammer2_lookup,
654 .fsread = boot2_hammer2_read
659 /************************************************************************
661 ************************************************************************
667 struct hammer2_fs hfs;
668 hammer2_blockref_t bref;
676 hammer2_get_dtype(uint8_t type)
679 case HAMMER2_OBJTYPE_DIRECTORY:
681 case HAMMER2_OBJTYPE_REGFILE:
683 case HAMMER2_OBJTYPE_FIFO:
685 case HAMMER2_OBJTYPE_CDEV:
687 case HAMMER2_OBJTYPE_BDEV:
689 case HAMMER2_OBJTYPE_SOFTLINK:
691 case HAMMER2_OBJTYPE_HARDLINK:
693 case HAMMER2_OBJTYPE_SOCKET:
702 hammer2_get_mode(uint8_t type)
705 case HAMMER2_OBJTYPE_DIRECTORY:
707 case HAMMER2_OBJTYPE_REGFILE:
709 case HAMMER2_OBJTYPE_FIFO:
711 case HAMMER2_OBJTYPE_CDEV:
713 case HAMMER2_OBJTYPE_BDEV:
715 case HAMMER2_OBJTYPE_SOFTLINK:
717 case HAMMER2_OBJTYPE_HARDLINK:
719 case HAMMER2_OBJTYPE_SOCKET:
727 hammer2_open(const char *path, struct open_file *f)
729 struct hfile *hf = malloc(sizeof(*hf));
730 hammer2_inode_data_t *ipdata;
732 bzero(hf, sizeof(*hf));
737 if (h2init(&hf->hfs)) {
743 h2resolve(&hf->hfs, path, &hf->bref, &ipdata);
744 if (hf->bref.data_off == (hammer2_off_t)-1 ||
745 (hf->bref.type != HAMMER2_BREF_TYPE_INODE &&
746 hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) {
753 hf->fsize = ipdata->size;
754 hf->type = ipdata->type;
755 hf->mode = ipdata->mode | hammer2_get_mode(ipdata->type);
758 hf->type = HAMMER2_OBJTYPE_DIRECTORY;
759 hf->mode = 0755 | S_IFDIR;
765 hammer2_close(struct open_file *f)
767 struct hfile *hf = f->f_fsdata;
776 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid)
778 struct hfile *hf = f->f_fsdata;
782 total = h2readfile(&hf->hfs, &hf->bref,
783 f->f_offset, hf->fsize, buf, len);
788 f->f_offset += total;
791 *resid = len - total;
796 hammer2_seek(struct open_file *f, off_t offset, int whence)
798 struct hfile *hf = f->f_fsdata;
802 f->f_offset = offset;
805 f->f_offset += offset;
808 f->f_offset = hf->fsize - offset;
813 return (f->f_offset);
817 hammer2_stat(struct open_file *f, struct stat *st)
819 struct hfile *hf = f->f_fsdata;
821 st->st_mode = hf->mode;
824 st->st_size = hf->fsize;
830 hammer2_readdir(struct open_file *f, struct dirent *den)
832 struct hfile *hf = f->f_fsdata;
833 hammer2_blockref_t bres;
834 hammer2_inode_data_t *ipdata;
838 bytes = h2lookup(&hf->hfs, &hf->bref,
839 f->f_offset | HAMMER2_DIRHASH_VISIBLE,
841 &bres, (void **)&ipdata);
844 den->d_namlen = ipdata->name_len;
845 den->d_type = hammer2_get_dtype(ipdata->type);
846 den->d_ino = ipdata->inum;
847 bcopy(ipdata->filename, den->d_name, den->d_namlen);
848 den->d_name[den->d_namlen] = 0;
850 f->f_offset = bres.key + 1;
857 struct fs_ops hammer_fsops = {