1 /* 2 * Copyright (c) 2010 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Ilya Dryomov <idryomov@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "hammer.h" 36 37 static __inline int validate_zone(hammer_off_t data_offset); 38 39 int 40 hammer_ioc_dedup(hammer_transaction_t trans, hammer_inode_t ip, 41 struct hammer_ioc_dedup *dedup) 42 { 43 struct hammer_cursor cursor1, cursor2; 44 int error; 45 46 /* 47 * Enforce hammer filesystem version requirements 48 */ 49 if (trans->hmp->version < HAMMER_VOL_VERSION_FIVE) { 50 kprintf("hammer: Filesystem must be upgraded to v5 " 51 "before you can run dedup\n"); 52 return (EOPNOTSUPP); 53 } 54 55 /* 56 * Cursor1, return an error -> candidate goes to pass2 list 57 */ 58 error = hammer_init_cursor(trans, &cursor1, NULL, NULL); 59 if (error) 60 goto done_cursor; 61 cursor1.key_beg = dedup->elm1; 62 cursor1.flags |= HAMMER_CURSOR_BACKEND; 63 64 error = hammer_btree_lookup(&cursor1); 65 if (error) 66 goto done_cursor; 67 error = hammer_btree_extract(&cursor1, HAMMER_CURSOR_GET_LEAF | 68 HAMMER_CURSOR_GET_DATA); 69 if (error) 70 goto done_cursor; 71 72 /* 73 * Cursor2, return an error -> candidate goes to pass2 list 74 */ 75 error = hammer_init_cursor(trans, &cursor2, NULL, NULL); 76 if (error) 77 goto done_cursors; 78 cursor2.key_beg = dedup->elm2; 79 cursor2.flags |= HAMMER_CURSOR_BACKEND; 80 81 error = hammer_btree_lookup(&cursor2); 82 if (error) 83 goto done_cursors; 84 error = hammer_btree_extract(&cursor2, HAMMER_CURSOR_GET_LEAF | 85 HAMMER_CURSOR_GET_DATA); 86 if (error) 87 goto done_cursors; 88 89 /* 90 * Zone validation. We can't de-dup any of the other zones 91 * (BTREE or META) or bad things will happen. 92 * 93 * Return with error = 0, but set an INVALID_ZONE flag. 94 */ 95 error = validate_zone(cursor1.leaf->data_offset) + 96 validate_zone(cursor2.leaf->data_offset); 97 if (error) { 98 dedup->head.flags |= HAMMER_IOC_DEDUP_INVALID_ZONE; 99 error = 0; 100 goto done_cursors; 101 } 102 103 /* 104 * Comparison checks 105 * 106 * If zones don't match or data_len fields aren't the same 107 * we consider it to be a comparison failure. 108 * 109 * Return with error = 0, but set a CMP_FAILURE flag. 110 */ 111 if ((cursor1.leaf->data_offset & HAMMER_OFF_ZONE_MASK) != 112 (cursor2.leaf->data_offset & HAMMER_OFF_ZONE_MASK)) { 113 dedup->head.flags |= HAMMER_IOC_DEDUP_CMP_FAILURE; 114 goto done_cursors; 115 } 116 if (cursor1.leaf->data_len != cursor2.leaf->data_len) { 117 dedup->head.flags |= HAMMER_IOC_DEDUP_CMP_FAILURE; 118 goto done_cursors; 119 } 120 121 /* byte-by-byte comparison to be sure */ 122 if (bcmp(cursor1.data, cursor2.data, cursor1.leaf->data_len)) { 123 dedup->head.flags |= HAMMER_IOC_DEDUP_CMP_FAILURE; 124 goto done_cursors; 125 } 126 127 /* 128 * Upgrade both cursors together to an exclusive lock 129 * 130 * Return an error -> candidate goes to pass2 list 131 */ 132 hammer_sync_lock_sh(trans); 133 error = hammer_cursor_upgrade2(&cursor1, &cursor2); 134 if (error) { 135 hammer_sync_unlock(trans); 136 goto done_cursors; 137 } 138 139 error = hammer_blockmap_dedup(cursor1.trans, 140 cursor1.leaf->data_offset, cursor1.leaf->data_len); 141 if (error) { 142 if (error == ERANGE) { 143 /* 144 * Return with error = 0, but set an UNDERFLOW flag 145 */ 146 dedup->head.flags |= HAMMER_IOC_DEDUP_UNDERFLOW; 147 error = 0; 148 goto downgrade_cursors; 149 } else { 150 /* 151 * Return an error -> block goes to pass2 list 152 */ 153 goto downgrade_cursors; 154 } 155 } 156 157 /* 158 * The cursor2's cache must be invalidated before calling 159 * hammer_blockmap_free(), otherwise it will not be able to 160 * invalidate the underlying data buffer. 161 */ 162 hammer_cursor_invalidate_cache(&cursor2); 163 hammer_blockmap_free(cursor2.trans, 164 cursor2.leaf->data_offset, cursor2.leaf->data_len); 165 166 hammer_modify_node(cursor2.trans, cursor2.node, 167 &cursor2.leaf->data_offset, sizeof(hammer_off_t)); 168 cursor2.leaf->data_offset = cursor1.leaf->data_offset; 169 hammer_modify_node_done(cursor2.node); 170 171 downgrade_cursors: 172 hammer_cursor_downgrade2(&cursor1, &cursor2); 173 hammer_sync_unlock(trans); 174 done_cursors: 175 hammer_done_cursor(&cursor2); 176 done_cursor: 177 hammer_done_cursor(&cursor1); 178 return (error); 179 } 180 181 static __inline int 182 validate_zone(hammer_off_t data_offset) 183 { 184 switch(data_offset & HAMMER_OFF_ZONE_MASK) { 185 case HAMMER_ZONE_LARGE_DATA: 186 case HAMMER_ZONE_SMALL_DATA: 187 return (0); 188 default: 189 return (1); 190 } 191 } 192