15e286361SAndreas Jaekel #include <sys/zfs_events.h>
25e286361SAndreas Jaekel #include <sys/zev_checksums.h>
35e286361SAndreas Jaekel #include <sys/fs/zev.h>
45e286361SAndreas Jaekel #include <sys/zfs_znode.h>
55e286361SAndreas Jaekel #include <sys/sha1.h>
65e286361SAndreas Jaekel #include <sys/avl.h>
75e286361SAndreas Jaekel #include <sys/sysmacros.h>
85e286361SAndreas Jaekel #include <sys/fs/zev.h>
9b9710123SAndreas Jaekel #include <sys/zfs_rlock.h>
1080d9297cSAndreas Jaekel #include <sys/list.h>
115e286361SAndreas Jaekel
125e286361SAndreas Jaekel typedef struct zev_sig_cache_chksums_t {
135e286361SAndreas Jaekel /* begin of key */
145e286361SAndreas Jaekel uint64_t offset_l1;
155e286361SAndreas Jaekel /* end of key */
165e286361SAndreas Jaekel avl_node_t avl_node;
175e286361SAndreas Jaekel uint8_t sigs[ZEV_L1_SIZE/ZEV_L0_SIZE][SHA1_DIGEST_LENGTH];
185e286361SAndreas Jaekel } zev_sig_cache_chksums_t;
195e286361SAndreas Jaekel
205e286361SAndreas Jaekel typedef struct zev_sig_cache_file_t {
215e286361SAndreas Jaekel /* begin of key */
225e286361SAndreas Jaekel uint64_t guid;
235e286361SAndreas Jaekel uint64_t ino;
245e286361SAndreas Jaekel uint64_t gen;
255e286361SAndreas Jaekel /* end of key */
265e286361SAndreas Jaekel uint32_t refcnt;
2780d9297cSAndreas Jaekel list_node_t lru_node;
285e286361SAndreas Jaekel avl_node_t avl_node;
295e286361SAndreas Jaekel avl_tree_t chksums;
305e286361SAndreas Jaekel } zev_sig_cache_file_t;
315e286361SAndreas Jaekel
325e286361SAndreas Jaekel typedef struct zev_sig_cache_t {
335e286361SAndreas Jaekel kmutex_t mutex;
345e286361SAndreas Jaekel uint64_t cache_size;
355e286361SAndreas Jaekel uint64_t max_cache_size;
365e286361SAndreas Jaekel uint64_t hits;
375e286361SAndreas Jaekel uint64_t misses;
3880d9297cSAndreas Jaekel list_t lru;
395e286361SAndreas Jaekel avl_tree_t files;
405e286361SAndreas Jaekel } zev_sig_cache_t;
415e286361SAndreas Jaekel
425e286361SAndreas Jaekel extern offset_t zfs_read_chunk_size; /* tuneable from zfs_vnops.c */
435e286361SAndreas Jaekel
445e286361SAndreas Jaekel static uint8_t all_zero_sig[SHA1_DIGEST_LENGTH] = {
455e286361SAndreas Jaekel 0x1c, 0xea, 0xf7, 0x3d, 0xf4, 0x0e, 0x53, 0x1d, 0xf3, 0xbf,
465e286361SAndreas Jaekel 0xb2, 0x6b, 0x4f, 0xb7, 0xcd, 0x95, 0xfb, 0x7b, 0xff, 0x1d
475e286361SAndreas Jaekel };
485e286361SAndreas Jaekel
495e286361SAndreas Jaekel static uint8_t unknown_sig[SHA1_DIGEST_LENGTH] = {
505e286361SAndreas Jaekel 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
515e286361SAndreas Jaekel 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
525e286361SAndreas Jaekel };
535e286361SAndreas Jaekel
545e286361SAndreas Jaekel static zev_sig_cache_t zev_sig_cache;
555e286361SAndreas Jaekel
565e286361SAndreas Jaekel static int
zev_cache_file_cmp(const void * entry_a,const void * entry_b)575e286361SAndreas Jaekel zev_cache_file_cmp(const void *entry_a, const void *entry_b)
585e286361SAndreas Jaekel {
595e286361SAndreas Jaekel const zev_sig_cache_file_t *a = entry_a;
605e286361SAndreas Jaekel const zev_sig_cache_file_t *b = entry_b;
615e286361SAndreas Jaekel
625e286361SAndreas Jaekel if (a->guid < b->guid)
635e286361SAndreas Jaekel return -1;
645e286361SAndreas Jaekel if (a->guid > b->guid)
655e286361SAndreas Jaekel return 1;
665e286361SAndreas Jaekel if (a->ino < b->ino)
675e286361SAndreas Jaekel return -1;
685e286361SAndreas Jaekel if (a->ino > b->ino)
695e286361SAndreas Jaekel return 1;
705e286361SAndreas Jaekel if (a->gen < b->gen)
715e286361SAndreas Jaekel return -1;
725e286361SAndreas Jaekel if (a->gen > b->gen)
735e286361SAndreas Jaekel return 1;
745e286361SAndreas Jaekel return 0;
755e286361SAndreas Jaekel }
765e286361SAndreas Jaekel
775e286361SAndreas Jaekel static int
zev_chksum_cache_cmp(const void * entry_a,const void * entry_b)785e286361SAndreas Jaekel zev_chksum_cache_cmp(const void *entry_a, const void *entry_b)
795e286361SAndreas Jaekel {
805e286361SAndreas Jaekel const zev_sig_cache_chksums_t *a = entry_a;
815e286361SAndreas Jaekel const zev_sig_cache_chksums_t *b = entry_b;
825e286361SAndreas Jaekel
835e286361SAndreas Jaekel if (a->offset_l1 < b->offset_l1)
845e286361SAndreas Jaekel return -1;
855e286361SAndreas Jaekel if (a->offset_l1 > b->offset_l1)
865e286361SAndreas Jaekel return 1;
875e286361SAndreas Jaekel return 0;
885e286361SAndreas Jaekel }
895e286361SAndreas Jaekel
905e286361SAndreas Jaekel /* must be called with zev_sig_cache.mutex held */
915e286361SAndreas Jaekel static void
zev_chksum_cache_file_free(zev_sig_cache_file_t * file)925e286361SAndreas Jaekel zev_chksum_cache_file_free(zev_sig_cache_file_t *file)
935e286361SAndreas Jaekel {
945e286361SAndreas Jaekel zev_sig_cache_chksums_t *cs;
955e286361SAndreas Jaekel void *c = NULL; /* cookie */
965e286361SAndreas Jaekel
975e286361SAndreas Jaekel /* remove from lru list */
9880d9297cSAndreas Jaekel list_remove(&zev_sig_cache.lru, file);
995e286361SAndreas Jaekel /* free resources */
1005e286361SAndreas Jaekel avl_remove(&zev_sig_cache.files, file);
1015e286361SAndreas Jaekel while ((cs = avl_destroy_nodes(&file->chksums, &c)) != NULL) {
1025e286361SAndreas Jaekel zev_sig_cache.cache_size -= sizeof(*cs);
1035e286361SAndreas Jaekel zev_free(cs, sizeof(*cs));
1045e286361SAndreas Jaekel }
1055e286361SAndreas Jaekel avl_destroy(&file->chksums);
1065e286361SAndreas Jaekel zev_free(file, sizeof(*file));
1075e286361SAndreas Jaekel zev_sig_cache.cache_size -= sizeof(*file);
1085e286361SAndreas Jaekel }
1095e286361SAndreas Jaekel
1105e286361SAndreas Jaekel void
zev_chksum_init(void)1115e286361SAndreas Jaekel zev_chksum_init(void)
1125e286361SAndreas Jaekel {
1135e286361SAndreas Jaekel memset(&zev_sig_cache, 0, sizeof(zev_sig_cache));
1145e286361SAndreas Jaekel mutex_init(&zev_sig_cache.mutex, NULL, MUTEX_DRIVER, NULL);
1155e286361SAndreas Jaekel avl_create(&zev_sig_cache.files, zev_cache_file_cmp,
1165e286361SAndreas Jaekel sizeof(zev_sig_cache_file_t),
1175e286361SAndreas Jaekel offsetof(zev_sig_cache_file_t, avl_node));
11880d9297cSAndreas Jaekel list_create(&zev_sig_cache.lru,
11980d9297cSAndreas Jaekel sizeof(zev_sig_cache_file_t),
12080d9297cSAndreas Jaekel offsetof(zev_sig_cache_file_t, lru_node));
1215e286361SAndreas Jaekel zev_sig_cache.max_cache_size = ZEV_CHKSUM_DEFAULT_CACHE_SIZE;
1225e286361SAndreas Jaekel }
1235e286361SAndreas Jaekel
1245e286361SAndreas Jaekel void
zev_chksum_fini(void)1255e286361SAndreas Jaekel zev_chksum_fini(void)
1265e286361SAndreas Jaekel {
1275e286361SAndreas Jaekel zev_sig_cache_file_t *file;
1285e286361SAndreas Jaekel
1295e286361SAndreas Jaekel mutex_destroy(&zev_sig_cache.mutex);
1305e286361SAndreas Jaekel while ((file = avl_first(&zev_sig_cache.files)) != NULL)
1315e286361SAndreas Jaekel zev_chksum_cache_file_free(file);
13280d9297cSAndreas Jaekel list_destroy(&zev_sig_cache.lru);
1335e286361SAndreas Jaekel avl_destroy(&zev_sig_cache.files);
1345e286361SAndreas Jaekel }
1355e286361SAndreas Jaekel
1365e286361SAndreas Jaekel static zev_sig_cache_file_t *
zev_chksum_cache_file_get_and_hold(znode_t * zp)1375e286361SAndreas Jaekel zev_chksum_cache_file_get_and_hold(znode_t *zp)
1385e286361SAndreas Jaekel {
1395e286361SAndreas Jaekel zev_sig_cache_file_t find_file;
1405e286361SAndreas Jaekel zev_sig_cache_file_t *file;
1415e286361SAndreas Jaekel avl_index_t where;
1425e286361SAndreas Jaekel
14316a2f000SSimon Klinkert find_file.guid =
14416a2f000SSimon Klinkert dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid;
1455e286361SAndreas Jaekel find_file.ino = zp->z_id;
1465e286361SAndreas Jaekel find_file.gen = zp->z_gen;
1475e286361SAndreas Jaekel
1485e286361SAndreas Jaekel mutex_enter(&zev_sig_cache.mutex);
1495e286361SAndreas Jaekel file = avl_find(&zev_sig_cache.files, &find_file, &where);
1505e286361SAndreas Jaekel if (!file) {
1515e286361SAndreas Jaekel file = zev_alloc(sizeof(*file));
1525e286361SAndreas Jaekel file->guid =
15316a2f000SSimon Klinkert dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid;
1545e286361SAndreas Jaekel file->ino = zp->z_id;
1555e286361SAndreas Jaekel file->gen = zp->z_gen;
1565e286361SAndreas Jaekel file->refcnt = 0;
1575e286361SAndreas Jaekel avl_create(&file->chksums, zev_chksum_cache_cmp,
1585e286361SAndreas Jaekel sizeof(zev_sig_cache_chksums_t),
1595e286361SAndreas Jaekel offsetof(zev_sig_cache_chksums_t, avl_node));
16080d9297cSAndreas Jaekel list_insert_head(&zev_sig_cache.lru, file);
1615e286361SAndreas Jaekel avl_insert(&zev_sig_cache.files, file, where);
1625e286361SAndreas Jaekel zev_sig_cache.cache_size += sizeof(*file);
1635e286361SAndreas Jaekel }
1645e286361SAndreas Jaekel file->refcnt++;
1655e286361SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
1665e286361SAndreas Jaekel return file;
1675e286361SAndreas Jaekel }
1685e286361SAndreas Jaekel
1695e286361SAndreas Jaekel static void
zev_chksum_cache_file_release(zev_sig_cache_file_t * file)1705e286361SAndreas Jaekel zev_chksum_cache_file_release(zev_sig_cache_file_t *file)
1715e286361SAndreas Jaekel {
1725e286361SAndreas Jaekel mutex_enter(&zev_sig_cache.mutex);
1735e286361SAndreas Jaekel
1745e286361SAndreas Jaekel /* We don't invalidate/free/destroy *file. Cache expiry does that */
1755e286361SAndreas Jaekel file->refcnt--;
1765e286361SAndreas Jaekel
1775e286361SAndreas Jaekel /* Move file to front of lru list */
17880d9297cSAndreas Jaekel list_remove(&zev_sig_cache.lru, file);
17980d9297cSAndreas Jaekel list_insert_head(&zev_sig_cache.lru, file);
1805e286361SAndreas Jaekel
1815e286361SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
1825e286361SAndreas Jaekel }
1835e286361SAndreas Jaekel
1845e286361SAndreas Jaekel static zev_sig_cache_chksums_t *
zev_chksum_cache_get_lv1_entry(zev_sig_cache_file_t * file,uint64_t off_l1)1855e286361SAndreas Jaekel zev_chksum_cache_get_lv1_entry(zev_sig_cache_file_t *file, uint64_t off_l1)
1865e286361SAndreas Jaekel {
1875e286361SAndreas Jaekel zev_sig_cache_chksums_t find_chksum;
1885e286361SAndreas Jaekel zev_sig_cache_chksums_t *cs;
1895e286361SAndreas Jaekel avl_index_t where;
1905e286361SAndreas Jaekel
19159938026SAndreas Jaekel mutex_enter(&zev_sig_cache.mutex);
19259938026SAndreas Jaekel
1935e286361SAndreas Jaekel find_chksum.offset_l1 = off_l1;
1945e286361SAndreas Jaekel cs = avl_find(&file->chksums, &find_chksum, &where);
1955e286361SAndreas Jaekel if (!cs) {
1965e286361SAndreas Jaekel cs = zev_zalloc(sizeof(*cs));
1975e286361SAndreas Jaekel cs->offset_l1 = off_l1;
1985e286361SAndreas Jaekel avl_insert(&file->chksums, cs, where);
1995e286361SAndreas Jaekel zev_sig_cache.cache_size += sizeof(*cs);
2005e286361SAndreas Jaekel }
20159938026SAndreas Jaekel
20259938026SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
20359938026SAndreas Jaekel
2045e286361SAndreas Jaekel return cs;
2055e286361SAndreas Jaekel }
2065e286361SAndreas Jaekel
2075e286361SAndreas Jaekel void
zev_chksum_stats(uint64_t * c_size,uint64_t * c_hits,uint64_t * c_misses)2085e286361SAndreas Jaekel zev_chksum_stats(uint64_t *c_size, uint64_t *c_hits, uint64_t *c_misses)
2095e286361SAndreas Jaekel {
2105e286361SAndreas Jaekel mutex_enter(&zev_sig_cache.mutex);
2115e286361SAndreas Jaekel *c_size = zev_sig_cache.cache_size;
2125e286361SAndreas Jaekel *c_hits = zev_sig_cache.hits;
2135e286361SAndreas Jaekel *c_misses = zev_sig_cache.misses;
2145e286361SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
2155e286361SAndreas Jaekel }
2165e286361SAndreas Jaekel
2175e286361SAndreas Jaekel static void
zev_chksum_cache_invalidate(zev_sig_cache_file_t * file,znode_t * zp,zev_chksum_mode_t mode,uint64_t off,uint64_t len)2185e286361SAndreas Jaekel zev_chksum_cache_invalidate(zev_sig_cache_file_t *file,
2195e286361SAndreas Jaekel znode_t *zp,
2205e286361SAndreas Jaekel zev_chksum_mode_t mode,
2215e286361SAndreas Jaekel uint64_t off,
2225e286361SAndreas Jaekel uint64_t len)
2235e286361SAndreas Jaekel {
2245e286361SAndreas Jaekel zev_sig_cache_chksums_t find_chksum;
2255e286361SAndreas Jaekel zev_sig_cache_chksums_t *cs;
2265e286361SAndreas Jaekel int idx;
2275e286361SAndreas Jaekel uint64_t off_l1;
2285e286361SAndreas Jaekel uint64_t len_l1;
2295e286361SAndreas Jaekel uint64_t pos_l0;
2305e286361SAndreas Jaekel uint64_t pos_l1;
2315e286361SAndreas Jaekel
2325e286361SAndreas Jaekel mutex_enter(&zev_sig_cache.mutex);
2335e286361SAndreas Jaekel
2345e286361SAndreas Jaekel /* start of this megabyte */
2355e286361SAndreas Jaekel off_l1 = P2ALIGN(off, ZEV_L1_SIZE);
2365e286361SAndreas Jaekel
2375e286361SAndreas Jaekel if (len == 0) {
2385e286361SAndreas Jaekel /* truncate() to EOF */
2395e286361SAndreas Jaekel len_l1 = ZEV_L1_SIZE;
2405e286361SAndreas Jaekel } else {
2415e286361SAndreas Jaekel /* full megabytes */
2425e286361SAndreas Jaekel len_l1 = len + (off - off_l1);
2435e286361SAndreas Jaekel len_l1 = P2ROUNDUP(len_l1, ZEV_L1_SIZE);
2445e286361SAndreas Jaekel }
2455e286361SAndreas Jaekel
2465e286361SAndreas Jaekel for (pos_l1 = off_l1; pos_l1 < (off_l1+len_l1); pos_l1 += ZEV_L1_SIZE) {
2475e286361SAndreas Jaekel
2485e286361SAndreas Jaekel find_chksum.offset_l1 = pos_l1;
2495e286361SAndreas Jaekel cs = avl_find(&file->chksums, &find_chksum, NULL);
2505e286361SAndreas Jaekel if (!cs)
2515e286361SAndreas Jaekel continue;
2525e286361SAndreas Jaekel
2535e286361SAndreas Jaekel for (pos_l0 = MAX(pos_l1, P2ALIGN(off, ZEV_L0_SIZE));
2545e286361SAndreas Jaekel pos_l0 < (pos_l1 + ZEV_L1_SIZE);
2555e286361SAndreas Jaekel pos_l0 += ZEV_L0_SIZE){
2565e286361SAndreas Jaekel
257f96935a8SAndreas Jaekel if ((len > 0) && (pos_l0 > (off + len - 1)))
2585e286361SAndreas Jaekel break;
2595e286361SAndreas Jaekel
2605e286361SAndreas Jaekel idx = (pos_l0 % ZEV_L1_SIZE) / ZEV_L0_SIZE;
2615e286361SAndreas Jaekel memcpy(cs->sigs[idx], unknown_sig, SHA1_DIGEST_LENGTH);
2625e286361SAndreas Jaekel }
2635e286361SAndreas Jaekel }
2645e286361SAndreas Jaekel
2655e286361SAndreas Jaekel if (len == 0) {
2665e286361SAndreas Jaekel /* truncate() to EOF -> invalidate all l1 sigs beyond EOF */
2675e286361SAndreas Jaekel while ((cs = avl_last(&file->chksums)) != NULL) {
2685e286361SAndreas Jaekel if (cs->offset_l1 < zp->z_size)
2695e286361SAndreas Jaekel break;
2705e286361SAndreas Jaekel avl_remove(&file->chksums, cs);
2715e286361SAndreas Jaekel zev_sig_cache.cache_size -= sizeof(*cs);
2725e286361SAndreas Jaekel zev_free(cs, sizeof(*cs));
2735e286361SAndreas Jaekel }
2745e286361SAndreas Jaekel }
2755e286361SAndreas Jaekel
2765e286361SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
2775e286361SAndreas Jaekel }
2785e286361SAndreas Jaekel
2795e286361SAndreas Jaekel static int
zev_chksum_cache_get(uint8_t * dst,zev_sig_cache_file_t * file,zev_sig_cache_chksums_t * cs,uint64_t off_l0)2805e286361SAndreas Jaekel zev_chksum_cache_get(uint8_t *dst,
2815e286361SAndreas Jaekel zev_sig_cache_file_t *file,
2825e286361SAndreas Jaekel zev_sig_cache_chksums_t *cs,
2835e286361SAndreas Jaekel uint64_t off_l0)
2845e286361SAndreas Jaekel {
2855e286361SAndreas Jaekel int idx;
2865e286361SAndreas Jaekel
2875e286361SAndreas Jaekel mutex_enter(&zev_sig_cache.mutex);
2885e286361SAndreas Jaekel
2895e286361SAndreas Jaekel idx = (off_l0 % ZEV_L1_SIZE) / ZEV_L0_SIZE;
2905e286361SAndreas Jaekel if (!memcmp(cs->sigs[idx], unknown_sig, SHA1_DIGEST_LENGTH)) {
2915e286361SAndreas Jaekel zev_sig_cache.misses++;
2925e286361SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
2935e286361SAndreas Jaekel return ENOENT;
2945e286361SAndreas Jaekel }
2955e286361SAndreas Jaekel memcpy(dst, cs->sigs[idx], SHA1_DIGEST_LENGTH);
2965e286361SAndreas Jaekel zev_sig_cache.hits++;
2975e286361SAndreas Jaekel
2985e286361SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
2995e286361SAndreas Jaekel return 0;
3005e286361SAndreas Jaekel }
3015e286361SAndreas Jaekel
3025e286361SAndreas Jaekel static void
zev_chksum_cache_put(uint8_t * sig,zev_sig_cache_file_t * file,zev_sig_cache_chksums_t * cs,uint64_t off_l0)3035e286361SAndreas Jaekel zev_chksum_cache_put(uint8_t *sig,
3045e286361SAndreas Jaekel zev_sig_cache_file_t *file,
3055e286361SAndreas Jaekel zev_sig_cache_chksums_t *cs,
3065e286361SAndreas Jaekel uint64_t off_l0)
3075e286361SAndreas Jaekel {
3085e286361SAndreas Jaekel zev_sig_cache_file_t *f;
309a0e77e28SAndreas Jaekel zev_sig_cache_file_t *tmp;
3105e286361SAndreas Jaekel int idx;
3115e286361SAndreas Jaekel
3125e286361SAndreas Jaekel mutex_enter(&zev_sig_cache.mutex);
3135e286361SAndreas Jaekel
3145e286361SAndreas Jaekel if (zev_sig_cache.max_cache_size == 0) {
3155e286361SAndreas Jaekel /* cache disabled */
3165e286361SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
3175e286361SAndreas Jaekel return;
3185e286361SAndreas Jaekel }
3195e286361SAndreas Jaekel
3205e286361SAndreas Jaekel /* expire entries until there's room in the cache */
32180d9297cSAndreas Jaekel f = list_tail(&zev_sig_cache.lru);
322a0e77e28SAndreas Jaekel while (f && (zev_sig_cache.cache_size > zev_sig_cache.max_cache_size)){
323a0e77e28SAndreas Jaekel tmp = f;
32480d9297cSAndreas Jaekel f = list_prev(&zev_sig_cache.lru, f);
325a0e77e28SAndreas Jaekel if (tmp->refcnt == 0)
326a0e77e28SAndreas Jaekel zev_chksum_cache_file_free(tmp);
3275e286361SAndreas Jaekel }
3285e286361SAndreas Jaekel
3295e286361SAndreas Jaekel idx = (off_l0 % ZEV_L1_SIZE) / ZEV_L0_SIZE;
3305e286361SAndreas Jaekel memcpy(cs->sigs[idx], sig, SHA1_DIGEST_LENGTH);
3315e286361SAndreas Jaekel
3325e286361SAndreas Jaekel mutex_exit(&zev_sig_cache.mutex);
3335e286361SAndreas Jaekel return;
3345e286361SAndreas Jaekel }
3355e286361SAndreas Jaekel
3365e286361SAndreas Jaekel /* verbatim from zfs_vnops.c (unfortunatly it's declared static, there) */
3375e286361SAndreas Jaekel static int
mappedread(vnode_t * vp,int nbytes,uio_t * uio)3385e286361SAndreas Jaekel mappedread(vnode_t *vp, int nbytes, uio_t *uio)
3395e286361SAndreas Jaekel {
3405e286361SAndreas Jaekel znode_t *zp = VTOZ(vp);
3415e286361SAndreas Jaekel objset_t *os = zp->z_zfsvfs->z_os;
3425e286361SAndreas Jaekel int64_t start, off;
3435e286361SAndreas Jaekel int len = nbytes;
3445e286361SAndreas Jaekel int error = 0;
3455e286361SAndreas Jaekel
3465e286361SAndreas Jaekel start = uio->uio_loffset;
3475e286361SAndreas Jaekel off = start & PAGEOFFSET;
3485e286361SAndreas Jaekel for (start &= PAGEMASK; len > 0; start += PAGESIZE) {
3495e286361SAndreas Jaekel page_t *pp;
3505e286361SAndreas Jaekel uint64_t bytes = MIN(PAGESIZE - off, len);
3515e286361SAndreas Jaekel
3525e286361SAndreas Jaekel if (pp = page_lookup(vp, start, SE_SHARED)) {
3535e286361SAndreas Jaekel caddr_t va;
3545e286361SAndreas Jaekel
3555e286361SAndreas Jaekel va = zfs_map_page(pp, S_READ);
3565e286361SAndreas Jaekel error = uiomove(va + off, bytes, UIO_READ, uio);
3575e286361SAndreas Jaekel zfs_unmap_page(pp, va);
3585e286361SAndreas Jaekel page_unlock(pp);
3595e286361SAndreas Jaekel } else {
3605e286361SAndreas Jaekel error = dmu_read_uio(os, zp->z_id, uio, bytes);
3615e286361SAndreas Jaekel }
3625e286361SAndreas Jaekel len -= bytes;
3635e286361SAndreas Jaekel off = 0;
3645e286361SAndreas Jaekel if (error)
3655e286361SAndreas Jaekel break;
3665e286361SAndreas Jaekel }
3675e286361SAndreas Jaekel return (error);
3685e286361SAndreas Jaekel }
3695e286361SAndreas Jaekel
3705e286361SAndreas Jaekel static int
zev_safe_read(znode_t * zp,char * buf,uint64_t off,uint64_t len)3715e286361SAndreas Jaekel zev_safe_read(znode_t *zp, char *buf, uint64_t off, uint64_t len)
3725e286361SAndreas Jaekel {
3735e286361SAndreas Jaekel uio_t uio;
3745e286361SAndreas Jaekel struct iovec iov;
3755e286361SAndreas Jaekel ssize_t n;
3765e286361SAndreas Jaekel ssize_t nbytes;
3775e286361SAndreas Jaekel int error = 0;
3785e286361SAndreas Jaekel vnode_t *vp = ZTOV(zp);
3795e286361SAndreas Jaekel objset_t *os = zp->z_zfsvfs->z_os;
3805e286361SAndreas Jaekel
3815e286361SAndreas Jaekel /* set up uio */
3825e286361SAndreas Jaekel
3835e286361SAndreas Jaekel iov.iov_base = buf;
3845e286361SAndreas Jaekel iov.iov_len = ZEV_L0_SIZE;
3855e286361SAndreas Jaekel
3865e286361SAndreas Jaekel uio.uio_iov = &iov;
3875e286361SAndreas Jaekel uio.uio_iovcnt = 1;
3885e286361SAndreas Jaekel uio.uio_segflg = (short)UIO_SYSSPACE;
3895e286361SAndreas Jaekel uio.uio_llimit = RLIM64_INFINITY;
3905e286361SAndreas Jaekel uio.uio_fmode = FREAD;
3915e286361SAndreas Jaekel uio.uio_extflg = UIO_COPY_DEFAULT;
3925e286361SAndreas Jaekel
3935e286361SAndreas Jaekel uio.uio_loffset = off;
3945e286361SAndreas Jaekel uio.uio_resid = len;
3955e286361SAndreas Jaekel
3965e286361SAndreas Jaekel again:
3975e286361SAndreas Jaekel if (uio.uio_loffset >= zp->z_size)
3985e286361SAndreas Jaekel return EINVAL;
3995e286361SAndreas Jaekel
4005e286361SAndreas Jaekel /* don't read past EOF */
4015e286361SAndreas Jaekel n = MIN(uio.uio_resid, zp->z_size - uio.uio_loffset);
4025e286361SAndreas Jaekel
4035e286361SAndreas Jaekel /* this block was essentially copied from zfs_read() in zfs_vnops.c */
4045e286361SAndreas Jaekel while (n > 0) {
4055e286361SAndreas Jaekel nbytes = MIN(n, zfs_read_chunk_size -
4065e286361SAndreas Jaekel P2PHASE(uio.uio_loffset, zfs_read_chunk_size));
4075e286361SAndreas Jaekel
4085e286361SAndreas Jaekel if (vn_has_cached_data(vp)) {
4095e286361SAndreas Jaekel error = mappedread(vp, nbytes, &uio);
4105e286361SAndreas Jaekel } else {
4115e286361SAndreas Jaekel error = dmu_read_uio(os, zp->z_id, &uio, nbytes);
4125e286361SAndreas Jaekel }
4135e286361SAndreas Jaekel if (error) {
414df8caf2dSSimon Klinkert if (error == EINTR)
4155e286361SAndreas Jaekel goto again;
4165e286361SAndreas Jaekel /* convert checksum errors into IO errors */
4175e286361SAndreas Jaekel if (error == ECKSUM)
4185e286361SAndreas Jaekel error = SET_ERROR(EIO);
4195e286361SAndreas Jaekel break;
4205e286361SAndreas Jaekel }
4215e286361SAndreas Jaekel
4225e286361SAndreas Jaekel n -= nbytes;
4235e286361SAndreas Jaekel }
4245e286361SAndreas Jaekel
4255e286361SAndreas Jaekel if (error)
4265e286361SAndreas Jaekel return error;
4275e286361SAndreas Jaekel return len - uio.uio_resid;
4285e286361SAndreas Jaekel }
4295e286361SAndreas Jaekel
4305e286361SAndreas Jaekel static void
zev_l0_sig(uint8_t * sig,char * buf)4315e286361SAndreas Jaekel zev_l0_sig(uint8_t *sig, char *buf)
4325e286361SAndreas Jaekel {
4335e286361SAndreas Jaekel SHA1_CTX ctx;
4345e286361SAndreas Jaekel
4355e286361SAndreas Jaekel SHA1Init(&ctx);
4365e286361SAndreas Jaekel SHA1Update(&ctx, buf, ZEV_L0_SIZE);
4375e286361SAndreas Jaekel SHA1Final(sig, &ctx);
4385e286361SAndreas Jaekel return;
4395e286361SAndreas Jaekel }
4405e286361SAndreas Jaekel
4415e286361SAndreas Jaekel static void
zev_l0_blocksig(uint8_t * blk_sig,uint8_t * l0_sig,uint8_t block_no)4425e286361SAndreas Jaekel zev_l0_blocksig(uint8_t *blk_sig, uint8_t *l0_sig, uint8_t block_no)
4435e286361SAndreas Jaekel {
4445e286361SAndreas Jaekel SHA1_CTX ctx;
4455e286361SAndreas Jaekel
4465e286361SAndreas Jaekel SHA1Init(&ctx);
4475e286361SAndreas Jaekel SHA1Update(&ctx, l0_sig, SHA1_DIGEST_LENGTH);
4485e286361SAndreas Jaekel SHA1Update(&ctx, &block_no, sizeof(block_no));
4495e286361SAndreas Jaekel SHA1Final(blk_sig, &ctx);
4505e286361SAndreas Jaekel return;
4515e286361SAndreas Jaekel }
4525e286361SAndreas Jaekel
4535e286361SAndreas Jaekel static void
zev_l1_add(uint8_t * sig_l1,uint8_t * sig_l0)4545e286361SAndreas Jaekel zev_l1_add(uint8_t *sig_l1, uint8_t *sig_l0)
4555e286361SAndreas Jaekel {
4565e286361SAndreas Jaekel int i;
4575e286361SAndreas Jaekel int s;
4585e286361SAndreas Jaekel int carry = 0;
4595e286361SAndreas Jaekel
4605e286361SAndreas Jaekel for (i = SHA1_DIGEST_LENGTH - 1; i >= 0; --i) {
4615e286361SAndreas Jaekel s = sig_l1[i] + sig_l0[i] + carry;
4625e286361SAndreas Jaekel carry = s > 255 ? 1 : 0;
4635e286361SAndreas Jaekel sig_l1[i] = s & 0xff;
4645e286361SAndreas Jaekel }
4655e286361SAndreas Jaekel }
4665e286361SAndreas Jaekel
467b9710123SAndreas Jaekel static int
zev_get_result_buffer(zev_sig_t ** buffer,uint64_t * buffer_len,uint64_t max_buffer_len,znode_t * zp,uint64_t off,uint64_t len,zev_chksum_mode_t mode)4685e286361SAndreas Jaekel zev_get_result_buffer(zev_sig_t **buffer,
4695e286361SAndreas Jaekel uint64_t *buffer_len,
470b9710123SAndreas Jaekel uint64_t max_buffer_len,
4715e286361SAndreas Jaekel znode_t *zp,
4725e286361SAndreas Jaekel uint64_t off,
4735e286361SAndreas Jaekel uint64_t len,
4745e286361SAndreas Jaekel zev_chksum_mode_t mode)
4755e286361SAndreas Jaekel {
4765e286361SAndreas Jaekel uint64_t blk_start;
4775e286361SAndreas Jaekel uint64_t blk_end;
4785e286361SAndreas Jaekel uint64_t l0_blocks;
4795e286361SAndreas Jaekel uint64_t l1_blocks;
4805e286361SAndreas Jaekel uint64_t sigs;
4815e286361SAndreas Jaekel int buflen;
4825e286361SAndreas Jaekel
4835e286361SAndreas Jaekel /* calculate result set size: how many checksums will we provide? */
4845e286361SAndreas Jaekel
4855e286361SAndreas Jaekel ASSERT(len > 0 || (mode == zev_truncate && len == 0));
4865e286361SAndreas Jaekel
4875e286361SAndreas Jaekel if (len == 0) {
4885e286361SAndreas Jaekel /* truncate */
4895e286361SAndreas Jaekel l0_blocks = ((off % ZEV_L0_SIZE) == 0) ? 0 : 1;
4905e286361SAndreas Jaekel l1_blocks = ((off % ZEV_L1_SIZE) == 0) ? 0 : 1;
4915e286361SAndreas Jaekel } else {
4925e286361SAndreas Jaekel /* how many lv1 checksums do we update? */
4935e286361SAndreas Jaekel blk_start = off / ZEV_L1_SIZE;
4945e286361SAndreas Jaekel blk_end = (off + len - 1) / ZEV_L1_SIZE;
4955e286361SAndreas Jaekel l1_blocks = blk_end - blk_start + 1;
4965e286361SAndreas Jaekel /* how many lv0 checksums do we update? */
4975e286361SAndreas Jaekel blk_start = off / ZEV_L0_SIZE;
4985e286361SAndreas Jaekel blk_end = (off + len - 1) / ZEV_L0_SIZE;
4995e286361SAndreas Jaekel l0_blocks = blk_end - blk_start + 1;
5005e286361SAndreas Jaekel }
5015e286361SAndreas Jaekel
5025e286361SAndreas Jaekel sigs = l1_blocks + l0_blocks;
5035e286361SAndreas Jaekel if (sigs == 0) {
5045e286361SAndreas Jaekel *buffer = NULL;
5055e286361SAndreas Jaekel *buffer_len = 0;
506b9710123SAndreas Jaekel return 0;
5075e286361SAndreas Jaekel }
5085e286361SAndreas Jaekel
5095e286361SAndreas Jaekel buflen = sigs * sizeof(zev_sig_t);
510b9710123SAndreas Jaekel if (max_buffer_len && (buflen > max_buffer_len)) {
511b9710123SAndreas Jaekel *buffer = NULL;
512b9710123SAndreas Jaekel *buffer_len = 0;
513b9710123SAndreas Jaekel return ENOSPC;
514b9710123SAndreas Jaekel }
5155e286361SAndreas Jaekel *buffer_len = buflen;
5165e286361SAndreas Jaekel *buffer = zev_alloc(buflen);
517b9710123SAndreas Jaekel return 0;
5185e286361SAndreas Jaekel }
5195e286361SAndreas Jaekel
5205e286361SAndreas Jaekel static void
zev_append_sig(zev_sig_t * s,int level,uint64_t off,uint8_t * sig)5215e286361SAndreas Jaekel zev_append_sig(zev_sig_t *s, int level, uint64_t off, uint8_t *sig)
5225e286361SAndreas Jaekel {
5235e286361SAndreas Jaekel s->level = level;
5245e286361SAndreas Jaekel s->block_offset = off;
5255e286361SAndreas Jaekel memcpy(s->value, sig, SHA1_DIGEST_LENGTH);
5265e286361SAndreas Jaekel }
5275e286361SAndreas Jaekel
5285e286361SAndreas Jaekel /*
5295e286361SAndreas Jaekel * Calculate all l0 and l1 checksums that are affected by the given range.
5305e286361SAndreas Jaekel *
5315e286361SAndreas Jaekel * This function assumes that the ranges it needs to read are already
5325e286361SAndreas Jaekel * range-locked.
5335e286361SAndreas Jaekel */
5345e286361SAndreas Jaekel int
zev_get_checksums(zev_sig_t ** result,uint64_t * result_buf_len,uint64_t * signature_cnt,uint64_t max_result_len,znode_t * zp,uint64_t off,uint64_t len,zev_chksum_mode_t mode)5355e286361SAndreas Jaekel zev_get_checksums(zev_sig_t **result,
5365e286361SAndreas Jaekel uint64_t *result_buf_len,
5375e286361SAndreas Jaekel uint64_t *signature_cnt,
538b9710123SAndreas Jaekel uint64_t max_result_len,
5395e286361SAndreas Jaekel znode_t *zp,
5405e286361SAndreas Jaekel uint64_t off,
5415e286361SAndreas Jaekel uint64_t len,
5425e286361SAndreas Jaekel zev_chksum_mode_t mode)
5435e286361SAndreas Jaekel {
5445e286361SAndreas Jaekel uint64_t off_l1;
5455e286361SAndreas Jaekel uint64_t len_l1;
5465e286361SAndreas Jaekel uint64_t pos_l1;
5475e286361SAndreas Jaekel uint64_t pos_l0;
5485e286361SAndreas Jaekel char *buf;
5495e286361SAndreas Jaekel int64_t ret;
5505e286361SAndreas Jaekel uint8_t sig_l0[SHA1_DIGEST_LENGTH];
5515e286361SAndreas Jaekel uint8_t blk_sig_l0[SHA1_DIGEST_LENGTH];
5525e286361SAndreas Jaekel uint8_t sig_l1[SHA1_DIGEST_LENGTH];
5535e286361SAndreas Jaekel uint8_t l0_block_no;
5545e286361SAndreas Jaekel zev_sig_t *sig;
5555e286361SAndreas Jaekel int non_empty_l0_blocks;
5565e286361SAndreas Jaekel zev_sig_cache_file_t *file;
5575e286361SAndreas Jaekel zev_sig_cache_chksums_t *cs;
5585e286361SAndreas Jaekel
5595e286361SAndreas Jaekel /*
5605e286361SAndreas Jaekel * Note: for write events, the callback is called via
5615e286361SAndreas Jaekel * zfs_write() -> zfs_log_write() -> zev_znode_write_cb()
5625e286361SAndreas Jaekel *
5635e286361SAndreas Jaekel * The transaction is not commited, yet.
5645e286361SAndreas Jaekel *
5655e286361SAndreas Jaekel * A write() syscall might be split into smaller chunks by zfs_write()
5665e286361SAndreas Jaekel *
5675e286361SAndreas Jaekel * zfs_write() has a range lock when this is called. (zfs_vnops.c:925)
5685e286361SAndreas Jaekel * In zev mode, the range lock will encompass all data we need
5695e286361SAndreas Jaekel * to calculate our checksums.
5705e286361SAndreas Jaekel *
5715e286361SAndreas Jaekel * The same is true for truncates with non-zero length. ("punch hole")
5725e286361SAndreas Jaekel */
5735e286361SAndreas Jaekel
5745e286361SAndreas Jaekel ASSERT(len > 0 || (mode == zev_truncate && len == 0));
5755e286361SAndreas Jaekel *signature_cnt = 0;
5765e286361SAndreas Jaekel
577c2200253SAndreas Jaekel /*
578c2200253SAndreas Jaekel * Under certain circumstances we need the first l0 block's
579c2200253SAndreas Jaekel * checksum, because we didn't store it in the database and
580c2200253SAndreas Jaekel * can't easily get it from userspace. Not for this exact point
581c2200253SAndreas Jaekel * in time, anyway. So we cheat a little.
582c2200253SAndreas Jaekel */
583c2200253SAndreas Jaekel if (mode == zev_truncate && len == 0 && off == 4096) {
584c2200253SAndreas Jaekel /*
585c2200253SAndreas Jaekel * Normally, we'd report no checkums:
586c2200253SAndreas Jaekel * - no l0 sum, because no remaining l0 block is changed
587c2200253SAndreas Jaekel * - no l1 sum, because the file is now too short for l1 sums
588c2200253SAndreas Jaekel * Let's pretend we changed the first l0 block, then.
589c2200253SAndreas Jaekel * Luckily the entire file is range locked during truncate().
590c2200253SAndreas Jaekel */
591c2200253SAndreas Jaekel off = 0;
592c2200253SAndreas Jaekel len = 4096;
593c2200253SAndreas Jaekel }
594c2200253SAndreas Jaekel
5955e286361SAndreas Jaekel /* start of this megabyte */
5965e286361SAndreas Jaekel off_l1 = P2ALIGN(off, ZEV_L1_SIZE);
5975e286361SAndreas Jaekel /* full megabytes */
5985e286361SAndreas Jaekel if (len == 0) {
5995e286361SAndreas Jaekel /* truncate(): we'll look at the last lv1 block, only. */
6005e286361SAndreas Jaekel len_l1 = ZEV_L1_SIZE;
6015e286361SAndreas Jaekel } else {
6025e286361SAndreas Jaekel len_l1 = len + (off - off_l1);
6035e286361SAndreas Jaekel len_l1 = P2ROUNDUP(len_l1, ZEV_L1_SIZE);
6045e286361SAndreas Jaekel }
6055e286361SAndreas Jaekel
6065e286361SAndreas Jaekel file = zev_chksum_cache_file_get_and_hold(zp);
6075e286361SAndreas Jaekel zev_chksum_cache_invalidate(file, zp, mode, off, len);
6085e286361SAndreas Jaekel buf = zev_alloc(ZEV_L0_SIZE);
6095e286361SAndreas Jaekel
610b9710123SAndreas Jaekel ret = zev_get_result_buffer(result, result_buf_len, max_result_len,
611b9710123SAndreas Jaekel zp, off, len, mode);
612b9710123SAndreas Jaekel if (ret) {
613b9710123SAndreas Jaekel zev_free(buf, ZEV_L0_SIZE);
614b9710123SAndreas Jaekel zev_chksum_cache_file_release(file);
615b9710123SAndreas Jaekel return ret;
616b9710123SAndreas Jaekel }
6175e286361SAndreas Jaekel if (*result == NULL) {
6185e286361SAndreas Jaekel /* we're done */
6195e286361SAndreas Jaekel zev_free(buf, ZEV_L0_SIZE);
6205e286361SAndreas Jaekel zev_chksum_cache_file_release(file);
6215e286361SAndreas Jaekel return 0;
6225e286361SAndreas Jaekel }
6235e286361SAndreas Jaekel sig = *result;
6245e286361SAndreas Jaekel
6255e286361SAndreas Jaekel for (pos_l1 = off_l1; pos_l1 < (off_l1+len_l1); pos_l1 += ZEV_L1_SIZE) {
6265e286361SAndreas Jaekel
6275e286361SAndreas Jaekel if (pos_l1 > zp->z_size) {
6285e286361SAndreas Jaekel cmn_err(CE_WARN, "zev_get_checksums: off+len beyond "
62986dc7a29SSimon Klinkert "EOF. Unexpected behaviour; please fix! "
63086dc7a29SSimon Klinkert "off=%" PRIu64 ", len=%" PRIu64 ", "
63186dc7a29SSimon Klinkert "dataset='%s', inode=%" PRIu64, off, len,
63286dc7a29SSimon Klinkert zp->z_zfsvfs->z_os->
633266747efSAndreas Jaekel os_dsl_dataset->ds_dir->dd_myname, zp->z_id);
634266747efSAndreas Jaekel zev_free(*result, *result_buf_len);
635266747efSAndreas Jaekel *result = NULL;
636*4053e902SSimon Klinkert zev_free(buf, ZEV_L0_SIZE);
637*4053e902SSimon Klinkert zev_chksum_cache_file_release(file);
638*4053e902SSimon Klinkert return EIO;
6395e286361SAndreas Jaekel }
6405e286361SAndreas Jaekel
6415e286361SAndreas Jaekel /*
6425e286361SAndreas Jaekel * Since we have a reference to 'file' 'cs' can't be expired.
6435e286361SAndreas Jaekel * Since our ranges are range locked, other threads woun't
6445e286361SAndreas Jaekel * touch our checksum entries. (not even read them)
6455e286361SAndreas Jaekel * Hence, we don't need to hold() or release() 'cs'.
6465e286361SAndreas Jaekel */
6475e286361SAndreas Jaekel cs = zev_chksum_cache_get_lv1_entry(file, pos_l1);
6485e286361SAndreas Jaekel
6495e286361SAndreas Jaekel l0_block_no = 0;
6505e286361SAndreas Jaekel non_empty_l0_blocks = 0;
6515e286361SAndreas Jaekel bzero(sig_l1, sizeof(sig_l1));
6525e286361SAndreas Jaekel for (pos_l0 = pos_l1;
6535e286361SAndreas Jaekel pos_l0 < (pos_l1 + ZEV_L1_SIZE);
6545e286361SAndreas Jaekel pos_l0 += ZEV_L0_SIZE){
6555e286361SAndreas Jaekel
6565e286361SAndreas Jaekel if (pos_l0 >= zp->z_size)
6575e286361SAndreas Jaekel break; /* EOF */
6585e286361SAndreas Jaekel
6595e286361SAndreas Jaekel if (zev_chksum_cache_get(sig_l0, file,cs,pos_l0) != 0) {
6605e286361SAndreas Jaekel
6615e286361SAndreas Jaekel /* signature is not cached, yet. */
6625e286361SAndreas Jaekel ret = zev_safe_read(zp, buf,
6635e286361SAndreas Jaekel pos_l0, ZEV_L0_SIZE);
6645e286361SAndreas Jaekel if (ret < 0) {
6655e286361SAndreas Jaekel zev_free(*result, *result_buf_len);
6665e286361SAndreas Jaekel zev_free(buf, ZEV_L0_SIZE);
6675e286361SAndreas Jaekel zev_chksum_cache_file_release(file);
6685e286361SAndreas Jaekel return ret;
6695e286361SAndreas Jaekel }
6705e286361SAndreas Jaekel /* pad buffer with zeros if necessary */
6715e286361SAndreas Jaekel if (ret < ZEV_L0_SIZE)
6725e286361SAndreas Jaekel bzero(buf + ret, ZEV_L0_SIZE - ret);
6735e286361SAndreas Jaekel
6745e286361SAndreas Jaekel /* calculate signature */
6755e286361SAndreas Jaekel zev_l0_sig(sig_l0, buf);
6765e286361SAndreas Jaekel
6775e286361SAndreas Jaekel zev_chksum_cache_put(sig_l0, file, cs, pos_l0);
6785e286361SAndreas Jaekel }
6795e286361SAndreas Jaekel
6805e286361SAndreas Jaekel if (!memcmp(sig_l0, all_zero_sig, SHA1_DIGEST_LENGTH)) {
6815e286361SAndreas Jaekel /* all-zero l0 block. omit signature. */
6825e286361SAndreas Jaekel l0_block_no++;
6835e286361SAndreas Jaekel continue;
6845e286361SAndreas Jaekel }
6855e286361SAndreas Jaekel non_empty_l0_blocks++;
6865e286361SAndreas Jaekel zev_l0_blocksig(blk_sig_l0, sig_l0, l0_block_no);
6875e286361SAndreas Jaekel zev_l1_add(sig_l1, blk_sig_l0);
6885e286361SAndreas Jaekel
6895e286361SAndreas Jaekel if (((pos_l0 + ZEV_L0_SIZE - 1) >= off) &&
690aae4944bSAndreas Jaekel (pos_l0 <= (off + len - 1))) {
6915e286361SAndreas Jaekel zev_append_sig(sig++, 0, pos_l0, sig_l0);
6925e286361SAndreas Jaekel }
6935e286361SAndreas Jaekel
6945e286361SAndreas Jaekel l0_block_no++;
6955e286361SAndreas Jaekel }
6965e286361SAndreas Jaekel
6975e286361SAndreas Jaekel if (non_empty_l0_blocks && (zp->z_size > ZEV_L0_SIZE))
6985e286361SAndreas Jaekel zev_append_sig(sig++, 1, pos_l1, sig_l1);
6995e286361SAndreas Jaekel }
7005e286361SAndreas Jaekel
7015e286361SAndreas Jaekel *signature_cnt = ((char *)sig - (char *)*result) / sizeof(zev_sig_t);
7025e286361SAndreas Jaekel
7035e286361SAndreas Jaekel zev_free(buf, ZEV_L0_SIZE);
7045e286361SAndreas Jaekel zev_chksum_cache_file_release(file);
7055e286361SAndreas Jaekel return 0;
7065e286361SAndreas Jaekel }
707b9710123SAndreas Jaekel
708b9710123SAndreas Jaekel int
zev_ioc_get_signatures(intptr_t arg,int mode)709b9710123SAndreas Jaekel zev_ioc_get_signatures(intptr_t arg, int mode)
710b9710123SAndreas Jaekel {
711b9710123SAndreas Jaekel zev_ioctl_get_signatures_t gs;
712b9710123SAndreas Jaekel file_t *fp;
713b9710123SAndreas Jaekel int ret = 0;
714b9710123SAndreas Jaekel znode_t *zp;
715b9710123SAndreas Jaekel zev_sig_t *sig_buf = NULL;
716b9710123SAndreas Jaekel uint64_t sig_buf_len;
717b9710123SAndreas Jaekel uint64_t sig_cnt = 0;
718b9710123SAndreas Jaekel uint64_t sig_len;
719b9710123SAndreas Jaekel char *dst;
720b9710123SAndreas Jaekel int range_locked = 0;
721b9710123SAndreas Jaekel rl_t *rl;
722b9710123SAndreas Jaekel ssize_t lock_off;
723b9710123SAndreas Jaekel ssize_t lock_len;
724a287bd3aSAndreas Jaekel struct zfsvfs *zfsvfs = NULL;
725b9710123SAndreas Jaekel
726b9710123SAndreas Jaekel if (ddi_copyin((void *)arg, &gs, sizeof(gs), mode) != 0)
727b9710123SAndreas Jaekel return EFAULT;
728b9710123SAndreas Jaekel fp = getf(gs.zev_fd);
729b9710123SAndreas Jaekel if (fp == NULL)
730b9710123SAndreas Jaekel return EBADF;
731b9710123SAndreas Jaekel if (fp->f_vnode->v_vfsp->vfs_fstype != zfsfstype) {
732b9710123SAndreas Jaekel ret = EINVAL;
733b9710123SAndreas Jaekel goto out;
734b9710123SAndreas Jaekel }
735a287bd3aSAndreas Jaekel zp = VTOZ(fp->f_vnode);
736a287bd3aSAndreas Jaekel
737a287bd3aSAndreas Jaekel /* modified version of ZFS_ENTER() macro - we need to clean up fp */
738a287bd3aSAndreas Jaekel zfsvfs = zp->z_zfsvfs;
739a287bd3aSAndreas Jaekel rrm_enter_read(&zfsvfs->z_teardown_lock, FTAG);
740a287bd3aSAndreas Jaekel if (zp->z_zfsvfs->z_unmounted) {
741a287bd3aSAndreas Jaekel ret = EIO;
742a287bd3aSAndreas Jaekel goto out;
743a287bd3aSAndreas Jaekel }
744a287bd3aSAndreas Jaekel /* modified version of ZFS_VERIFY_ZP() macro */
745a287bd3aSAndreas Jaekel if (zp->z_sa_hdl == NULL) {
746a287bd3aSAndreas Jaekel ret = EIO;
747a287bd3aSAndreas Jaekel goto out;
748a287bd3aSAndreas Jaekel }
749a287bd3aSAndreas Jaekel
750b9710123SAndreas Jaekel if (fp->f_vnode->v_type != VREG) {
751b9710123SAndreas Jaekel ret = EINVAL;
752b9710123SAndreas Jaekel goto out;
753b9710123SAndreas Jaekel }
754b9710123SAndreas Jaekel if (gs.zev_offset >= zp->z_size) {
755b9710123SAndreas Jaekel ret = EINVAL;
756b9710123SAndreas Jaekel goto out;
757b9710123SAndreas Jaekel }
758b9710123SAndreas Jaekel
759b9710123SAndreas Jaekel /* range lock data */
760b9710123SAndreas Jaekel lock_off = P2ALIGN(gs.zev_offset, ZEV_L1_SIZE);
761b9710123SAndreas Jaekel lock_len = gs.zev_len + (gs.zev_offset - lock_off);
762b9710123SAndreas Jaekel lock_len = P2ROUNDUP(lock_len, ZEV_L1_SIZE);
763b9710123SAndreas Jaekel rl = zfs_range_lock(zp, lock_off, lock_len, RL_READER);
764b9710123SAndreas Jaekel range_locked = 1;
765b9710123SAndreas Jaekel
766b9710123SAndreas Jaekel /* get checksums */
767b9710123SAndreas Jaekel ret = zev_get_checksums(&sig_buf, &sig_buf_len, &sig_cnt,
768b9710123SAndreas Jaekel gs.zev_bufsize,
769b9710123SAndreas Jaekel zp, gs.zev_offset, gs.zev_len, zev_write);
770b9710123SAndreas Jaekel if (ret)
771b9710123SAndreas Jaekel goto out;
772b9710123SAndreas Jaekel
773b9710123SAndreas Jaekel /* copy to userland */
774b9710123SAndreas Jaekel sig_len = sig_cnt * sizeof(zev_sig_t);
775b9710123SAndreas Jaekel gs.zev_signature_cnt = sig_cnt;
776b9710123SAndreas Jaekel if (ddi_copyout(&gs, (void *)arg, sizeof(gs), mode) != 0) {
777b9710123SAndreas Jaekel ret = EFAULT;
778b9710123SAndreas Jaekel goto out;
779b9710123SAndreas Jaekel }
780b9710123SAndreas Jaekel if (sig_cnt && sig_buf) {
781b9710123SAndreas Jaekel dst = (char *)arg + sizeof(gs);
782b9710123SAndreas Jaekel if (ddi_copyout(sig_buf, (void *)dst, sig_len, mode) != 0) {
783b9710123SAndreas Jaekel ret = EFAULT;
784b9710123SAndreas Jaekel goto out;
785b9710123SAndreas Jaekel }
786b9710123SAndreas Jaekel }
787b9710123SAndreas Jaekel out:
788b9710123SAndreas Jaekel if (sig_buf)
789b9710123SAndreas Jaekel zev_free(sig_buf, sig_buf_len);
790b9710123SAndreas Jaekel if (range_locked)
791b9710123SAndreas Jaekel zfs_range_unlock(rl);
792a287bd3aSAndreas Jaekel if (zfsvfs)
793a287bd3aSAndreas Jaekel ZFS_EXIT(zfsvfs);
794b9710123SAndreas Jaekel releasef(gs.zev_fd);
795b9710123SAndreas Jaekel return ret;
796b9710123SAndreas Jaekel }
797b9710123SAndreas Jaekel
7981ca5a13bSAndreas Jaekel void
zev_symlink_checksum(zev_znode_symlink_t * rec,char * link)7991ca5a13bSAndreas Jaekel zev_symlink_checksum(zev_znode_symlink_t *rec, char *link)
8001ca5a13bSAndreas Jaekel {
8011ca5a13bSAndreas Jaekel char buf[ZEV_L0_SIZE];
8021ca5a13bSAndreas Jaekel
8031ca5a13bSAndreas Jaekel memset(buf, 0, sizeof(buf));
8041ca5a13bSAndreas Jaekel strcpy(buf, link);
8051ca5a13bSAndreas Jaekel zev_l0_sig(rec->signature.value, buf);
8061ca5a13bSAndreas Jaekel rec->signature.level = 0;
8071ca5a13bSAndreas Jaekel rec->signature.block_offset = 0;
8081ca5a13bSAndreas Jaekel }
8091ca5a13bSAndreas Jaekel
8101ca5a13bSAndreas Jaekel
8111ca5a13bSAndreas Jaekel void
zev_create_checksum(zev_znode_create_t * rec,znode_t * zp)8121ca5a13bSAndreas Jaekel zev_create_checksum(zev_znode_create_t *rec, znode_t *zp)
8131ca5a13bSAndreas Jaekel {
8141ca5a13bSAndreas Jaekel char buf[ZEV_L0_SIZE];
8151ca5a13bSAndreas Jaekel vnode_t *vp;
8161ca5a13bSAndreas Jaekel uint64_t rdev;
8171ca5a13bSAndreas Jaekel
8181ca5a13bSAndreas Jaekel vp = ZTOV(zp);
8191ca5a13bSAndreas Jaekel if (vp->v_type == VBLK || vp->v_type == VCHR) {
8201ca5a13bSAndreas Jaekel sa_lookup(zp->z_sa_hdl, SA_ZPL_RDEV(zp->z_zfsvfs),
8211ca5a13bSAndreas Jaekel &rdev, sizeof(rdev));
8221ca5a13bSAndreas Jaekel memset(buf, 0, sizeof(buf));
8231ca5a13bSAndreas Jaekel snprintf(buf, sizeof(buf), "%c%d,%d",
8241ca5a13bSAndreas Jaekel vp->v_type == VBLK ? 'b' : 'c',
8251ca5a13bSAndreas Jaekel getmajor(rdev),
8261ca5a13bSAndreas Jaekel getminor(rdev));
8271ca5a13bSAndreas Jaekel zev_l0_sig(rec->signature.value, buf);
8281ca5a13bSAndreas Jaekel } else {
8291ca5a13bSAndreas Jaekel memset(rec->signature.value, 0, sizeof(rec->signature.value));
8301ca5a13bSAndreas Jaekel }
8311ca5a13bSAndreas Jaekel rec->signature.level = 0;
8321ca5a13bSAndreas Jaekel rec->signature.block_offset = 0;
8331ca5a13bSAndreas Jaekel }
8341ca5a13bSAndreas Jaekel
835