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 /* BIOS interface may barf on 64KB reads */
229 while (nbytes
> 16384) {
230 rc
= dskread(buf
, off
>> DEV_BSHIFT
, 16384 >> DEV_BSHIFT
);
232 buf
= (char *)buf
+ 16384;
236 rc
= dskread(buf
, off
>> DEV_BSHIFT
, nbytes
>> DEV_BSHIFT
);
240 #error "hammer2: unknown library API"
248 * Initialize for HAMMER2 filesystem access given a hammer2_fs with
249 * its device file descriptor initialized.
253 * Lookup within the block specified by (*base), loading the block from disk
254 * if necessary. Locate the first key within the requested range and
255 * recursively run through indirect blocks as needed. The caller may loop
256 * by setting key_beg to *key_ret.
258 * Returns 0 if nothing could be found and the key range has been exhausted.
259 * returns -1 if a disk error occured. Otherwise returns the size of the
260 * data block and returns the data block in *pptr and bref in *bref_ret.
262 * NOTE! When reading file data, the returned bref's key will be the nearest
263 * data block we looked up. The file read procedure must handle any
264 * zero-fill or skip. However, we will truncate the return value to
268 h2lookup(struct hammer2_fs
*hfs
, hammer2_blockref_t
*base
,
269 hammer2_key_t key_beg
, hammer2_key_t key_end
,
270 hammer2_blockref_t
*bref_ret
, void **pptr
)
272 hammer2_blockref_t
*bref
;
273 hammer2_blockref_t best
;
274 hammer2_key_t scan_beg
;
275 hammer2_key_t scan_end
;
283 saved_base
.data_off
= (hammer2_off_t
)-1;
286 if (base
->data_off
== (hammer2_off_t
)-1)
290 * Calculate the number of blockrefs to scan
293 case HAMMER2_BREF_TYPE_VOLUME
:
294 count
= HAMMER2_SET_COUNT
;
296 case HAMMER2_BREF_TYPE_INODE
:
297 count
= HAMMER2_SET_COUNT
;
299 case HAMMER2_BREF_TYPE_INDIRECT
:
300 count
= blocksize(base
) / sizeof(hammer2_blockref_t
);
305 * Find the best candidate (the lowest blockref within the specified
306 * range). The array can be fully set associative so make no ordering
310 best
.key
= HAMMER2_KEY_MAX
;
313 for (i
= 0; i
< count
; ++i
) {
315 * [re]load when returning from our recursion
317 if (base
->type
!= HAMMER2_BREF_TYPE_VOLUME
&&
318 base
->data_off
!= saved_base
.data_off
) {
319 if (h2read(hfs
, &media
,
320 blocksize(base
), blockoff(base
))) {
327 * Special case embedded file data
329 if (base
->type
== HAMMER2_BREF_TYPE_INODE
) {
330 if (media
.ipdata
.meta
.op_flags
&
331 HAMMER2_OPFLAG_DIRECTDATA
) {
332 *pptr
= media
.ipdata
.u
.data
;
333 bref_ret
->type
= HAMMER2_BREF_TYPE_DATA
;
335 return HAMMER2_EMBEDDED_BYTES
;
340 * Calculate the bref in our scan.
343 case HAMMER2_BREF_TYPE_VOLUME
:
344 bref
= &hfs
->sroot_blockset
.blockref
[i
];
346 case HAMMER2_BREF_TYPE_INODE
:
347 bref
= &media
.ipdata
.u
.blockset
.blockref
[i
];
349 case HAMMER2_BREF_TYPE_INDIRECT
:
350 bref
= &media
.npdata
[i
];
355 if (bref
->key
> best
.key
)
357 scan_beg
= bref
->key
;
358 scan_end
= scan_beg
+ ((hammer2_key_t
)1 << bref
->keybits
) - 1;
359 if (scan_end
>= key_beg
&& scan_beg
<= key_end
) {
365 * Figure out what to do with the results.
374 case HAMMER2_BREF_TYPE_INDIRECT
:
376 * Matched an indirect block. If the block turns out to
377 * contain nothing we continue the iteration, otherwise
378 * we return the data from the recursion.
380 * Be sure to handle the overflow case when recalculating
383 rc
= h2lookup(hfs
, &best
, key_beg
, key_end
, bref_ret
, pptr
);
386 ((hammer2_key_t
)1 << best
.keybits
);
387 if (key_beg
> best
.key
&& key_beg
<= key_end
)
391 case HAMMER2_BREF_TYPE_INODE
:
392 case HAMMER2_BREF_TYPE_DATA
:
394 * Terminal match. Leaf elements might not be data-aligned.
396 dev_bsize
= blocksize(&best
);
397 if (dev_bsize
< HAMMER2_LBUFSIZE
)
398 dev_bsize
= HAMMER2_LBUFSIZE
;
399 dev_boff
= blockoff(&best
) -
400 (blockoff(&best
) & ~HAMMER2_LBUFMASK64
);
401 if (h2read(hfs
, &media
, dev_bsize
, blockoff(&best
) - dev_boff
))
403 saved_base
.data_off
= (hammer2_off_t
)-1;
405 *pptr
= media
.buf
+ dev_boff
;
406 rc
= blocksize(&best
);
414 h2resolve(struct hammer2_fs
*hfs
, const char *path
,
415 hammer2_blockref_t
*bref
, hammer2_inode_data_t
**inop
)
417 hammer2_blockref_t bres
;
418 hammer2_inode_data_t
*ino
;
424 * Start point (superroot)
431 * Iterate path elements
436 if (*path
== 0) /* terminal */
440 * Calculate path element and look for it in the directory
442 for (len
= 0; path
[len
]; ++len
) {
443 if (path
[len
] == '/')
446 key
= hammer2_dirhash(path
, len
);
448 bytes
= h2lookup(hfs
, bref
,
450 &bres
, (void **)&ino
);
453 if (len
== ino
->meta
.name_len
&&
454 memcmp(path
, ino
->filename
, len
) == 0) {
466 bref
->data_off
= (hammer2_off_t
)-1;
471 * Check path continuance, inode must be a directory or
475 if (*path
&& ino
->meta
.type
!= HAMMER2_OBJTYPE_DIRECTORY
) {
476 bref
->data_off
= (hammer2_off_t
)-1;
485 h2readfile(struct hammer2_fs
*hfs
, hammer2_blockref_t
*bref
,
486 off_t off
, off_t filesize
, void *buf
, size_t len
)
488 hammer2_blockref_t bres
;
499 if (off
+ len
> filesize
)
500 len
= filesize
- off
;
508 * Find closest bres >= requested offset.
510 bytes
= h2lookup(hfs
, bref
, off
, off
+ len
- 1,
511 &bres
, (void **)&data
);
520 * Load the data into the buffer. First handle a degenerate
530 * Returned record overlaps to the left of the requested
531 * position. It must overlap in this case or h2lookup()
532 * would have returned something else.
534 if (bres
.key
< off
) {
535 data
+= off
- bres
.key
;
536 bytes
-= off
- bres
.key
;
540 * Returned record overlaps to the right of the requested
541 * position, handle zero-fill. Again h2lookup() only returns
542 * this case if there is an actual overlap.
544 if (bres
.key
> off
) {
545 zfill
= (ssize_t
)(bres
.key
- off
);
550 buf
= (char *)buf
+ zfill
;
554 * Trim returned request before copying.
558 bcopy(data
, buf
, bytes
);
562 buf
= (char *)buf
+ bytes
;
569 h2init(struct hammer2_fs
*hfs
)
574 hammer2_tid_t best_tid
= 0;
582 * Find the best volume header.
584 * WARNING BIOS BUGS: It looks like some BIOSes will implode when
585 * given a disk offset beyond the EOM. XXX We need to probe the
586 * size of the media and limit our accesses, until then we have
587 * to give up if the first volume header does not have a hammer2
590 * XXX Probably still going to be problems w/ HAMMER2 volumes on
591 * media which is too small w/certain BIOSes.
594 for (i
= 0; i
< HAMMER2_NUM_VOLHDRS
; ++i
) {
595 off
= i
* HAMMER2_ZONE_BYTES64
;
598 if (h2read(hfs
, &media
, sizeof(media
.voldata
), off
))
600 if (media
.voldata
.magic
!= HAMMER2_VOLUME_ID_HBO
)
602 if (best
< 0 || best_tid
< media
.voldata
.mirror_tid
) {
604 best_tid
= media
.voldata
.mirror_tid
;
612 * Reload the best volume header and set up the blockref.
613 * We messed with media, clear the cache before continuing.
615 off
= best
* HAMMER2_ZONE_BYTES64
;
616 if (h2read(hfs
, &media
, sizeof(media
.voldata
), off
))
618 hfs
->sroot
.type
= HAMMER2_BREF_TYPE_VOLUME
;
619 hfs
->sroot
.data_off
= off
;
620 hfs
->sroot_blockset
= media
.voldata
.sroot_blockset
;
621 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
624 * Lookup sroot/BOOT and clear the cache again.
626 r
= h2lookup(hfs
, &hfs
->sroot
,
627 HAMMER2_SROOT_KEY
, HAMMER2_SROOT_KEY
,
631 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
632 r
= h2lookup(hfs
, &hfs
->sroot
,
633 HAMMER2_BOOT_KEY
, HAMMER2_BOOT_KEY
,
636 printf("hammer2: 'BOOT' PFS not found\n");
639 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
644 /************************************************************************
646 ************************************************************************
651 static struct hammer2_fs hfs
;
654 boot2_hammer2_init(void)
662 boot2_hammer2_lookup(const char *path
)
664 hammer2_blockref_t bref
;
666 h2resolve(&hfs
, path
, &bref
, NULL
);
667 return ((boot2_ino_t
)bref
.data_off
);
671 boot2_hammer2_read(boot2_ino_t ino
, void *buf
, size_t len
)
673 hammer2_blockref_t bref
;
676 bzero(&bref
, sizeof(bref
));
677 bref
.type
= HAMMER2_BREF_TYPE_INODE
;
680 total
= h2readfile(&hfs
, &bref
, fs_off
, 0x7FFFFFFF, buf
, len
);
686 const struct boot2_fsapi boot2_hammer2_api
= {
687 .fsinit
= boot2_hammer2_init
,
688 .fslookup
= boot2_hammer2_lookup
,
689 .fsread
= boot2_hammer2_read
694 /************************************************************************
696 ************************************************************************
702 struct hammer2_fs hfs
;
703 hammer2_blockref_t bref
;
711 hammer2_get_dtype(uint8_t type
)
714 case HAMMER2_OBJTYPE_DIRECTORY
:
716 case HAMMER2_OBJTYPE_REGFILE
:
718 case HAMMER2_OBJTYPE_FIFO
:
720 case HAMMER2_OBJTYPE_CDEV
:
722 case HAMMER2_OBJTYPE_BDEV
:
724 case HAMMER2_OBJTYPE_SOFTLINK
:
726 case HAMMER2_OBJTYPE_HARDLINK
:
728 case HAMMER2_OBJTYPE_SOCKET
:
737 hammer2_get_mode(uint8_t type
)
740 case HAMMER2_OBJTYPE_DIRECTORY
:
742 case HAMMER2_OBJTYPE_REGFILE
:
744 case HAMMER2_OBJTYPE_FIFO
:
746 case HAMMER2_OBJTYPE_CDEV
:
748 case HAMMER2_OBJTYPE_BDEV
:
750 case HAMMER2_OBJTYPE_SOFTLINK
:
752 case HAMMER2_OBJTYPE_HARDLINK
:
754 case HAMMER2_OBJTYPE_SOCKET
:
762 hammer2_open(const char *path
, struct open_file
*f
)
764 struct hfile
*hf
= malloc(sizeof(*hf
));
765 hammer2_inode_data_t
*ipdata
;
767 bzero(hf
, sizeof(*hf
));
772 if (h2init(&hf
->hfs
)) {
778 h2resolve(&hf
->hfs
, path
, &hf
->bref
, &ipdata
);
779 if (hf
->bref
.data_off
== (hammer2_off_t
)-1 ||
780 (hf
->bref
.type
!= HAMMER2_BREF_TYPE_INODE
&&
781 hf
->bref
.type
!= HAMMER2_BREF_TYPE_VOLUME
)) {
788 hf
->fsize
= ipdata
->meta
.size
;
789 hf
->type
= ipdata
->meta
.type
;
790 hf
->mode
= ipdata
->meta
.mode
|
791 hammer2_get_mode(ipdata
->meta
.type
);
794 hf
->type
= HAMMER2_OBJTYPE_DIRECTORY
;
795 hf
->mode
= 0755 | S_IFDIR
;
801 hammer2_close(struct open_file
*f
)
803 struct hfile
*hf
= f
->f_fsdata
;
812 hammer2_read(struct open_file
*f
, void *buf
, size_t len
, size_t *resid
)
814 struct hfile
*hf
= f
->f_fsdata
;
818 total
= h2readfile(&hf
->hfs
, &hf
->bref
,
819 f
->f_offset
, hf
->fsize
, buf
, len
);
824 f
->f_offset
+= total
;
827 *resid
= len
- total
;
832 hammer2_seek(struct open_file
*f
, off_t offset
, int whence
)
834 struct hfile
*hf
= f
->f_fsdata
;
838 f
->f_offset
= offset
;
841 f
->f_offset
+= offset
;
844 f
->f_offset
= hf
->fsize
- offset
;
849 return (f
->f_offset
);
853 hammer2_stat(struct open_file
*f
, struct stat
*st
)
855 struct hfile
*hf
= f
->f_fsdata
;
857 st
->st_mode
= hf
->mode
;
860 st
->st_size
= hf
->fsize
;
866 hammer2_readdir(struct open_file
*f
, struct dirent
*den
)
868 struct hfile
*hf
= f
->f_fsdata
;
869 hammer2_blockref_t bres
;
870 hammer2_inode_data_t
*ipdata
;
874 bytes
= h2lookup(&hf
->hfs
, &hf
->bref
,
875 f
->f_offset
| HAMMER2_DIRHASH_VISIBLE
,
877 &bres
, (void **)&ipdata
);
880 den
->d_namlen
= ipdata
->meta
.name_len
;
881 den
->d_type
= hammer2_get_dtype(ipdata
->meta
.type
);
882 den
->d_ino
= ipdata
->meta
.inum
;
883 bcopy(ipdata
->filename
, den
->d_name
, den
->d_namlen
);
884 den
->d_name
[den
->d_namlen
] = 0;
886 f
->f_offset
= bres
.key
+ 1;
893 struct fs_ops hammer_fsops
= {