2 * Copyright (c) 2010 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Ilya Dryomov <idryomov@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
37 static __inline int validate_zone(hammer_off_t data_offset);
40 hammer_ioc_dedup(hammer_transaction_t trans, hammer_inode_t ip,
41 struct hammer_ioc_dedup *dedup)
43 struct hammer_cursor cursor1, cursor2;
47 * Enforce hammer filesystem version requirements
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");
56 * Cursor1, return an error -> candidate goes to pass2 list
58 error = hammer_init_cursor(trans, &cursor1, NULL, NULL);
61 cursor1.key_beg = dedup->elm1;
62 cursor1.flags |= HAMMER_CURSOR_BACKEND;
64 error = hammer_btree_lookup(&cursor1);
67 error = hammer_btree_extract(&cursor1, HAMMER_CURSOR_GET_LEAF |
68 HAMMER_CURSOR_GET_DATA);
73 * Cursor2, return an error -> candidate goes to pass2 list
75 error = hammer_init_cursor(trans, &cursor2, NULL, NULL);
78 cursor2.key_beg = dedup->elm2;
79 cursor2.flags |= HAMMER_CURSOR_BACKEND;
81 error = hammer_btree_lookup(&cursor2);
84 error = hammer_btree_extract(&cursor2, HAMMER_CURSOR_GET_LEAF |
85 HAMMER_CURSOR_GET_DATA);
90 * Zone validation. We can't de-dup any of the other zones
91 * (BTREE or META) or bad things will happen.
93 * Return with error = 0, but set an INVALID_ZONE flag.
95 error = validate_zone(cursor1.leaf->data_offset) +
96 validate_zone(cursor2.leaf->data_offset);
98 dedup->head.flags |= HAMMER_IOC_DEDUP_INVALID_ZONE;
106 * If zones don't match or data_len fields aren't the same
107 * we consider it to be a comparison failure.
109 * Return with error = 0, but set a CMP_FAILURE flag.
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;
116 if (cursor1.leaf->data_len != cursor2.leaf->data_len) {
117 dedup->head.flags |= HAMMER_IOC_DEDUP_CMP_FAILURE;
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;
128 * Upgrade both cursors together to an exclusive lock
130 * Return an error -> candidate goes to pass2 list
132 hammer_sync_lock_sh(trans);
133 error = hammer_cursor_upgrade2(&cursor1, &cursor2);
135 hammer_sync_unlock(trans);
139 error = hammer_blockmap_dedup(cursor1.trans,
140 cursor1.leaf->data_offset, cursor1.leaf->data_len);
142 if (error == ERANGE) {
144 * Return with error = 0, but set an UNDERFLOW flag
146 dedup->head.flags |= HAMMER_IOC_DEDUP_UNDERFLOW;
148 goto downgrade_cursors;
151 * Return an error -> block goes to pass2 list
153 goto downgrade_cursors;
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.
162 hammer_cursor_invalidate_cache(&cursor2);
163 hammer_blockmap_free(cursor2.trans,
164 cursor2.leaf->data_offset, cursor2.leaf->data_len);
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);
172 hammer_cursor_downgrade2(&cursor1, &cursor2);
173 hammer_sync_unlock(trans);
175 hammer_done_cursor(&cursor2);
177 hammer_done_cursor(&cursor1);
182 validate_zone(hammer_off_t data_offset)
184 switch(data_offset & HAMMER_OFF_ZONE_MASK) {
185 case HAMMER_ZONE_LARGE_DATA:
186 case HAMMER_ZONE_SMALL_DATA: