2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.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
34 * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.c,v 1.32 2008/11/13 02:23:29 dillon Exp $
39 static int hammer_ioc_gethistory(hammer_transaction_t trans
, hammer_inode_t ip
,
40 struct hammer_ioc_history
*hist
);
41 static int hammer_ioc_synctid(hammer_transaction_t trans
, hammer_inode_t ip
,
42 struct hammer_ioc_synctid
*std
);
43 static int hammer_ioc_get_version(hammer_transaction_t trans
,
45 struct hammer_ioc_version
*ver
);
46 static int hammer_ioc_set_version(hammer_transaction_t trans
,
48 struct hammer_ioc_version
*ver
);
51 hammer_ioctl(hammer_inode_t ip
, u_long com
, caddr_t data
, int fflag
,
54 struct hammer_transaction trans
;
57 error
= suser_cred(cred
, PRISON_ROOT
);
59 hammer_start_transaction(&trans
, ip
->hmp
);
64 error
= hammer_ioc_prune(&trans
, ip
,
65 (struct hammer_ioc_prune
*)data
);
68 case HAMMERIOC_GETHISTORY
:
69 error
= hammer_ioc_gethistory(&trans
, ip
,
70 (struct hammer_ioc_history
*)data
);
72 case HAMMERIOC_REBLOCK
:
74 error
= hammer_ioc_reblock(&trans
, ip
,
75 (struct hammer_ioc_reblock
*)data
);
78 case HAMMERIOC_SYNCTID
:
79 error
= hammer_ioc_synctid(&trans
, ip
,
80 (struct hammer_ioc_synctid
*)data
);
82 case HAMMERIOC_GET_PSEUDOFS
:
83 error
= hammer_ioc_get_pseudofs(&trans
, ip
,
84 (struct hammer_ioc_pseudofs_rw
*)data
);
86 case HAMMERIOC_SET_PSEUDOFS
:
88 error
= hammer_ioc_set_pseudofs(&trans
, ip
, cred
,
89 (struct hammer_ioc_pseudofs_rw
*)data
);
92 case HAMMERIOC_UPG_PSEUDOFS
:
94 error
= hammer_ioc_upgrade_pseudofs(&trans
, ip
,
95 (struct hammer_ioc_pseudofs_rw
*)data
);
98 case HAMMERIOC_DGD_PSEUDOFS
:
100 error
= hammer_ioc_downgrade_pseudofs(&trans
, ip
,
101 (struct hammer_ioc_pseudofs_rw
*)data
);
104 case HAMMERIOC_RMR_PSEUDOFS
:
106 error
= hammer_ioc_destroy_pseudofs(&trans
, ip
,
107 (struct hammer_ioc_pseudofs_rw
*)data
);
110 case HAMMERIOC_WAI_PSEUDOFS
:
112 error
= hammer_ioc_wait_pseudofs(&trans
, ip
,
113 (struct hammer_ioc_pseudofs_rw
*)data
);
116 case HAMMERIOC_MIRROR_READ
:
118 error
= hammer_ioc_mirror_read(&trans
, ip
,
119 (struct hammer_ioc_mirror_rw
*)data
);
122 case HAMMERIOC_MIRROR_WRITE
:
124 error
= hammer_ioc_mirror_write(&trans
, ip
,
125 (struct hammer_ioc_mirror_rw
*)data
);
128 case HAMMERIOC_GET_VERSION
:
129 error
= hammer_ioc_get_version(&trans
, ip
,
130 (struct hammer_ioc_version
*)data
);
132 case HAMMERIOC_SET_VERSION
:
134 error
= hammer_ioc_set_version(&trans
, ip
,
135 (struct hammer_ioc_version
*)data
);
142 hammer_done_transaction(&trans
);
147 * Iterate through an object's inode or an object's records and record
150 static void add_history(hammer_inode_t ip
, struct hammer_ioc_history
*hist
,
151 hammer_btree_elm_t elm
);
155 hammer_ioc_gethistory(hammer_transaction_t trans
, hammer_inode_t ip
,
156 struct hammer_ioc_history
*hist
)
158 struct hammer_cursor cursor
;
159 hammer_btree_elm_t elm
;
163 * Validate the structure and initialize for return.
165 if (hist
->beg_tid
> hist
->end_tid
)
167 if (hist
->head
.flags
& HAMMER_IOC_HISTORY_ATKEY
) {
168 if (hist
->key
> hist
->nxt_key
)
172 hist
->obj_id
= ip
->obj_id
;
174 hist
->nxt_tid
= hist
->end_tid
;
175 hist
->head
.flags
&= ~HAMMER_IOC_HISTORY_NEXT_TID
;
176 hist
->head
.flags
&= ~HAMMER_IOC_HISTORY_NEXT_KEY
;
177 hist
->head
.flags
&= ~HAMMER_IOC_HISTORY_EOF
;
178 hist
->head
.flags
&= ~HAMMER_IOC_HISTORY_UNSYNCED
;
179 if ((ip
->flags
& HAMMER_INODE_MODMASK
) &
180 ~(HAMMER_INODE_ATIME
| HAMMER_INODE_MTIME
)) {
181 hist
->head
.flags
|= HAMMER_IOC_HISTORY_UNSYNCED
;
185 * Setup the cursor. We can't handle undeletable records
186 * (create_tid of 0) at the moment. A create_tid of 0 has
187 * a special meaning and cannot be specified in the cursor.
189 error
= hammer_init_cursor(trans
, &cursor
, &ip
->cache
[0], NULL
);
191 hammer_done_cursor(&cursor
);
195 cursor
.key_beg
.obj_id
= hist
->obj_id
;
196 cursor
.key_beg
.create_tid
= hist
->beg_tid
;
197 cursor
.key_beg
.delete_tid
= 0;
198 cursor
.key_beg
.obj_type
= 0;
199 if (cursor
.key_beg
.create_tid
== HAMMER_MIN_TID
)
200 cursor
.key_beg
.create_tid
= 1;
202 cursor
.key_end
.obj_id
= hist
->obj_id
;
203 cursor
.key_end
.create_tid
= hist
->end_tid
;
204 cursor
.key_end
.delete_tid
= 0;
205 cursor
.key_end
.obj_type
= 0;
207 cursor
.flags
|= HAMMER_CURSOR_END_EXCLUSIVE
;
209 if (hist
->head
.flags
& HAMMER_IOC_HISTORY_ATKEY
) {
211 * key-range within the file. For a regular file the
212 * on-disk key represents BASE+LEN, not BASE, so the
213 * first possible record containing the offset 'key'
214 * has an on-disk key of (key + 1).
216 cursor
.key_beg
.key
= hist
->key
;
217 cursor
.key_end
.key
= HAMMER_MAX_KEY
;
218 cursor
.key_beg
.localization
= ip
->obj_localization
+
219 HAMMER_LOCALIZE_MISC
;
220 cursor
.key_end
.localization
= ip
->obj_localization
+
221 HAMMER_LOCALIZE_MISC
;
223 switch(ip
->ino_data
.obj_type
) {
224 case HAMMER_OBJTYPE_REGFILE
:
225 ++cursor
.key_beg
.key
;
226 cursor
.key_beg
.rec_type
= HAMMER_RECTYPE_DATA
;
228 case HAMMER_OBJTYPE_DIRECTORY
:
229 cursor
.key_beg
.rec_type
= HAMMER_RECTYPE_DIRENTRY
;
231 case HAMMER_OBJTYPE_DBFILE
:
232 cursor
.key_beg
.rec_type
= HAMMER_RECTYPE_DB
;
238 cursor
.key_end
.rec_type
= cursor
.key_beg
.rec_type
;
243 cursor
.key_beg
.key
= 0;
244 cursor
.key_end
.key
= 0;
245 cursor
.key_beg
.rec_type
= HAMMER_RECTYPE_INODE
;
246 cursor
.key_end
.rec_type
= HAMMER_RECTYPE_INODE
;
247 cursor
.key_beg
.localization
= ip
->obj_localization
+
248 HAMMER_LOCALIZE_INODE
;
249 cursor
.key_end
.localization
= ip
->obj_localization
+
250 HAMMER_LOCALIZE_INODE
;
253 error
= hammer_btree_first(&cursor
);
255 elm
= &cursor
.node
->ondisk
->elms
[cursor
.index
];
257 add_history(ip
, hist
, elm
);
258 if (hist
->head
.flags
& (HAMMER_IOC_HISTORY_NEXT_TID
|
259 HAMMER_IOC_HISTORY_NEXT_KEY
|
260 HAMMER_IOC_HISTORY_EOF
)) {
263 error
= hammer_btree_iterate(&cursor
);
265 if (error
== ENOENT
) {
266 hist
->head
.flags
|= HAMMER_IOC_HISTORY_EOF
;
269 hammer_done_cursor(&cursor
);
274 * Add the scanned element to the ioctl return structure. Some special
275 * casing is required for regular files to accomodate how data ranges are
279 add_history(hammer_inode_t ip
, struct hammer_ioc_history
*hist
,
280 hammer_btree_elm_t elm
)
284 if (elm
->base
.btype
!= HAMMER_BTREE_TYPE_RECORD
)
286 if ((hist
->head
.flags
& HAMMER_IOC_HISTORY_ATKEY
) &&
287 ip
->ino_data
.obj_type
== HAMMER_OBJTYPE_REGFILE
) {
291 if (hist
->nxt_key
> elm
->leaf
.base
.key
- elm
->leaf
.data_len
&&
292 hist
->key
< elm
->leaf
.base
.key
- elm
->leaf
.data_len
) {
293 hist
->nxt_key
= elm
->leaf
.base
.key
- elm
->leaf
.data_len
;
295 if (hist
->nxt_key
> elm
->leaf
.base
.key
)
296 hist
->nxt_key
= elm
->leaf
.base
.key
;
299 * Record is beyond MAXPHYS, there won't be any more records
300 * in the iteration covering the requested offset (key).
302 if (elm
->leaf
.base
.key
>= MAXPHYS
&&
303 elm
->leaf
.base
.key
- MAXPHYS
> hist
->key
) {
304 hist
->head
.flags
|= HAMMER_IOC_HISTORY_NEXT_KEY
;
308 * Data-range of record does not cover the key.
310 if (elm
->leaf
.base
.key
- elm
->leaf
.data_len
> hist
->key
)
313 } else if (hist
->head
.flags
& HAMMER_IOC_HISTORY_ATKEY
) {
317 if (hist
->nxt_key
> elm
->leaf
.base
.key
&&
318 hist
->key
< elm
->leaf
.base
.key
) {
319 hist
->nxt_key
= elm
->leaf
.base
.key
;
323 * Record is beyond the requested key.
325 if (elm
->leaf
.base
.key
> hist
->key
)
326 hist
->head
.flags
|= HAMMER_IOC_HISTORY_NEXT_KEY
;
330 * Add create_tid if it is in-bounds.
334 elm
->leaf
.base
.create_tid
!= hist
->hist_ary
[i
- 1].tid
) &&
335 elm
->leaf
.base
.create_tid
>= hist
->beg_tid
&&
336 elm
->leaf
.base
.create_tid
< hist
->end_tid
) {
337 if (hist
->count
== HAMMER_MAX_HISTORY_ELMS
) {
338 hist
->nxt_tid
= elm
->leaf
.base
.create_tid
;
339 hist
->head
.flags
|= HAMMER_IOC_HISTORY_NEXT_TID
;
342 hist
->hist_ary
[i
].tid
= elm
->leaf
.base
.create_tid
;
343 hist
->hist_ary
[i
].time32
= elm
->leaf
.create_ts
;
348 * Add delete_tid if it is in-bounds. Note that different portions
349 * of the history may have overlapping data ranges with different
350 * delete_tid's. If this case occurs the delete_tid may match the
351 * create_tid of a following record. XXX
357 if (elm
->leaf
.base
.delete_tid
&&
358 elm
->leaf
.base
.delete_tid
>= hist
->beg_tid
&&
359 elm
->leaf
.base
.delete_tid
< hist
->end_tid
) {
360 if (i
== HAMMER_MAX_HISTORY_ELMS
) {
361 hist
->nxt_tid
= elm
->leaf
.base
.delete_tid
;
362 hist
->head
.flags
|= HAMMER_IOC_HISTORY_NEXT_TID
;
365 hist
->hist_ary
[i
].tid
= elm
->leaf
.base
.delete_tid
;
366 hist
->hist_ary
[i
].time32
= elm
->leaf
.delete_ts
;
372 * Acquire synchronization TID
376 hammer_ioc_synctid(hammer_transaction_t trans
, hammer_inode_t ip
,
377 struct hammer_ioc_synctid
*std
)
379 hammer_mount_t hmp
= ip
->hmp
;
383 case HAMMER_SYNCTID_NONE
:
384 std
->tid
= hmp
->flusher
.tid
; /* inaccurate */
386 case HAMMER_SYNCTID_ASYNC
:
387 hammer_queue_inodes_flusher(hmp
, MNT_NOWAIT
);
388 hammer_flusher_async(hmp
, NULL
);
389 std
->tid
= hmp
->flusher
.tid
; /* inaccurate */
391 case HAMMER_SYNCTID_SYNC1
:
392 hammer_queue_inodes_flusher(hmp
, MNT_WAIT
);
393 hammer_flusher_sync(hmp
);
394 std
->tid
= hmp
->flusher
.tid
;
396 case HAMMER_SYNCTID_SYNC2
:
397 hammer_queue_inodes_flusher(hmp
, MNT_WAIT
);
398 hammer_flusher_sync(hmp
);
399 std
->tid
= hmp
->flusher
.tid
;
400 hammer_flusher_sync(hmp
);
410 * Retrieve version info.
412 * Load min_version, wip_version, and max_versino. If cur_version is passed
413 * as 0 then load the current version into cur_version. Load the description
414 * for cur_version into the description array.
416 * Returns 0 on success, EINVAL if cur_version is non-zero and set to an
421 hammer_ioc_get_version(hammer_transaction_t trans
, hammer_inode_t ip
,
422 struct hammer_ioc_version
*ver
)
426 ver
->min_version
= HAMMER_VOL_VERSION_MIN
;
427 ver
->wip_version
= HAMMER_VOL_VERSION_WIP
;
428 ver
->max_version
= HAMMER_VOL_VERSION_MAX
;
429 if (ver
->cur_version
== 0)
430 ver
->cur_version
= trans
->hmp
->version
;
431 switch(ver
->cur_version
) {
433 ksnprintf(ver
->description
, sizeof(ver
->description
),
434 "2.0 - First HAMMER release");
437 ksnprintf(ver
->description
, sizeof(ver
->description
),
438 "2.2 - New directory hash");
441 ksnprintf(ver
->description
, sizeof(ver
->description
),
454 hammer_ioc_set_version(hammer_transaction_t trans
, hammer_inode_t ip
,
455 struct hammer_ioc_version
*ver
)
457 struct hammer_cursor cursor
;
458 hammer_volume_t volume
;
461 if (ver
->cur_version
< trans
->hmp
->version
)
463 if (ver
->cur_version
== trans
->hmp
->version
)
465 if (ver
->cur_version
> HAMMER_VOL_VERSION_MAX
)
467 if (trans
->hmp
->ronly
)
471 * Update the root volume header and the version cached in
472 * the hammer_mount structure.
474 error
= hammer_init_cursor(trans
, &cursor
, NULL
, NULL
);
477 hammer_sync_lock_sh(trans
);
479 volume
= hammer_get_root_volume(cursor
.trans
->hmp
, &error
);
480 KKASSERT(error
== 0);
481 hammer_modify_volume_field(cursor
.trans
, volume
, vol_version
);
482 volume
->ondisk
->vol_version
= ver
->cur_version
;
483 cursor
.trans
->hmp
->version
= ver
->cur_version
;
484 hammer_modify_volume_done(volume
);
485 hammer_rel_volume(volume
, 0);
487 hammer_sync_unlock(trans
);
489 ver
->head
.error
= error
;
490 hammer_done_cursor(&cursor
);