xref: /dflybsd-src/sys/vfs/hammer/hammer_ioctl.c (revision 201c8c4447cad562e0a54ebbe0e7ee4e8a0be647)
1 /*
2  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.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  * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.c,v 1.32 2008/11/13 02:23:29 dillon Exp $
35  */
36 
37 #include "hammer.h"
38 
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,
44 				hammer_inode_t ip,
45 				struct hammer_ioc_version *ver);
46 static int hammer_ioc_set_version(hammer_transaction_t trans,
47 				hammer_inode_t ip,
48 				struct hammer_ioc_version *ver);
49 static int hammer_ioc_get_info(hammer_transaction_t trans,
50 				struct hammer_ioc_info *info);
51 
52 
53 
54 int
55 hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
56 	     struct ucred *cred)
57 {
58 	struct hammer_transaction trans;
59 	int error;
60 
61 	error = priv_check_cred(cred, PRIV_ROOT, PRISON_ROOT);
62 
63 	hammer_start_transaction(&trans, ip->hmp);
64 
65 	switch(com) {
66 	case HAMMERIOC_PRUNE:
67 		if (error == 0) {
68 			error = hammer_ioc_prune(&trans, ip,
69 					(struct hammer_ioc_prune *)data);
70 		}
71 		break;
72 	case HAMMERIOC_GETHISTORY:
73 		error = hammer_ioc_gethistory(&trans, ip,
74 					(struct hammer_ioc_history *)data);
75 		break;
76 	case HAMMERIOC_REBLOCK:
77 		if (error == 0) {
78 			error = hammer_ioc_reblock(&trans, ip,
79 					(struct hammer_ioc_reblock *)data);
80 		}
81 		break;
82 	case HAMMERIOC_REBALANCE:
83 		if (error == 0) {
84 			error = hammer_ioc_rebalance(&trans, ip,
85 					(struct hammer_ioc_rebalance *)data);
86 		}
87 		break;
88 	case HAMMERIOC_SYNCTID:
89 		error = hammer_ioc_synctid(&trans, ip,
90 					(struct hammer_ioc_synctid *)data);
91 		break;
92 	case HAMMERIOC_GET_PSEUDOFS:
93 		error = hammer_ioc_get_pseudofs(&trans, ip,
94 				    (struct hammer_ioc_pseudofs_rw *)data);
95 		break;
96 	case HAMMERIOC_SET_PSEUDOFS:
97 		if (error == 0) {
98 			error = hammer_ioc_set_pseudofs(&trans, ip, cred,
99 				    (struct hammer_ioc_pseudofs_rw *)data);
100 		}
101 		break;
102 	case HAMMERIOC_UPG_PSEUDOFS:
103 		if (error == 0) {
104 			error = hammer_ioc_upgrade_pseudofs(&trans, ip,
105 				    (struct hammer_ioc_pseudofs_rw *)data);
106 		}
107 		break;
108 	case HAMMERIOC_DGD_PSEUDOFS:
109 		if (error == 0) {
110 			error = hammer_ioc_downgrade_pseudofs(&trans, ip,
111 				    (struct hammer_ioc_pseudofs_rw *)data);
112 		}
113 		break;
114 	case HAMMERIOC_RMR_PSEUDOFS:
115 		if (error == 0) {
116 			error = hammer_ioc_destroy_pseudofs(&trans, ip,
117 				    (struct hammer_ioc_pseudofs_rw *)data);
118 		}
119 		break;
120 	case HAMMERIOC_WAI_PSEUDOFS:
121 		if (error == 0) {
122 			error = hammer_ioc_wait_pseudofs(&trans, ip,
123 				    (struct hammer_ioc_pseudofs_rw *)data);
124 		}
125 		break;
126 	case HAMMERIOC_MIRROR_READ:
127 		if (error == 0) {
128 			error = hammer_ioc_mirror_read(&trans, ip,
129 				    (struct hammer_ioc_mirror_rw *)data);
130 		}
131 		break;
132 	case HAMMERIOC_MIRROR_WRITE:
133 		if (error == 0) {
134 			error = hammer_ioc_mirror_write(&trans, ip,
135 				    (struct hammer_ioc_mirror_rw *)data);
136 		}
137 		break;
138 	case HAMMERIOC_GET_VERSION:
139 		error = hammer_ioc_get_version(&trans, ip,
140 				    (struct hammer_ioc_version *)data);
141 		break;
142 	case HAMMERIOC_GET_INFO:
143 		error = hammer_ioc_get_info(&trans,
144 				    (struct hammer_ioc_info *)data);
145 		break;
146 	case HAMMERIOC_SET_VERSION:
147 		if (error == 0) {
148 			error = hammer_ioc_set_version(&trans, ip,
149 					    (struct hammer_ioc_version *)data);
150 		}
151 		break;
152 	default:
153 		error = EOPNOTSUPP;
154 		break;
155 	}
156 	hammer_done_transaction(&trans);
157 	return (error);
158 }
159 
160 /*
161  * Iterate through an object's inode or an object's records and record
162  * modification TIDs.
163  */
164 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
165 			hammer_btree_elm_t elm);
166 
167 static
168 int
169 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip,
170 		      struct hammer_ioc_history *hist)
171 {
172 	struct hammer_cursor cursor;
173 	hammer_btree_elm_t elm;
174 	int error;
175 
176 	/*
177 	 * Validate the structure and initialize for return.
178 	 */
179 	if (hist->beg_tid > hist->end_tid)
180 		return(EINVAL);
181 	if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
182 		if (hist->key > hist->nxt_key)
183 			return(EINVAL);
184 	}
185 
186 	hist->obj_id = ip->obj_id;
187 	hist->count = 0;
188 	hist->nxt_tid = hist->end_tid;
189 	hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID;
190 	hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY;
191 	hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF;
192 	hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED;
193 	if ((ip->flags & HAMMER_INODE_MODMASK) &
194 	    ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) {
195 		hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED;
196 	}
197 
198 	/*
199 	 * Setup the cursor.  We can't handle undeletable records
200 	 * (create_tid of 0) at the moment.  A create_tid of 0 has
201 	 * a special meaning and cannot be specified in the cursor.
202 	 */
203 	error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
204 	if (error) {
205 		hammer_done_cursor(&cursor);
206 		return(error);
207 	}
208 
209 	cursor.key_beg.obj_id = hist->obj_id;
210 	cursor.key_beg.create_tid = hist->beg_tid;
211 	cursor.key_beg.delete_tid = 0;
212 	cursor.key_beg.obj_type = 0;
213 	if (cursor.key_beg.create_tid == HAMMER_MIN_TID)
214 		cursor.key_beg.create_tid = 1;
215 
216 	cursor.key_end.obj_id = hist->obj_id;
217 	cursor.key_end.create_tid = hist->end_tid;
218 	cursor.key_end.delete_tid = 0;
219 	cursor.key_end.obj_type = 0;
220 
221 	cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE;
222 
223 	if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
224 		/*
225 		 * key-range within the file.  For a regular file the
226 		 * on-disk key represents BASE+LEN, not BASE, so the
227 		 * first possible record containing the offset 'key'
228 		 * has an on-disk key of (key + 1).
229 		 */
230 		cursor.key_beg.key = hist->key;
231 		cursor.key_end.key = HAMMER_MAX_KEY;
232 		cursor.key_beg.localization = ip->obj_localization +
233 					      HAMMER_LOCALIZE_MISC;
234 		cursor.key_end.localization = ip->obj_localization +
235 					      HAMMER_LOCALIZE_MISC;
236 
237 		switch(ip->ino_data.obj_type) {
238 		case HAMMER_OBJTYPE_REGFILE:
239 			++cursor.key_beg.key;
240 			cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
241 			break;
242 		case HAMMER_OBJTYPE_DIRECTORY:
243 			cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
244 			cursor.key_beg.localization =
245 						hammer_dir_localization(ip);
246 			cursor.key_end.localization =
247 						hammer_dir_localization(ip);
248 			break;
249 		case HAMMER_OBJTYPE_DBFILE:
250 			cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
251 			break;
252 		default:
253 			error = EINVAL;
254 			break;
255 		}
256 		cursor.key_end.rec_type = cursor.key_beg.rec_type;
257 	} else {
258 		/*
259 		 * The inode itself.
260 		 */
261 		cursor.key_beg.key = 0;
262 		cursor.key_end.key = 0;
263 		cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
264 		cursor.key_end.rec_type = HAMMER_RECTYPE_INODE;
265 		cursor.key_beg.localization = ip->obj_localization +
266 					      HAMMER_LOCALIZE_INODE;
267 		cursor.key_end.localization = ip->obj_localization +
268 					      HAMMER_LOCALIZE_INODE;
269 	}
270 
271 	error = hammer_btree_first(&cursor);
272 	while (error == 0) {
273 		elm = &cursor.node->ondisk->elms[cursor.index];
274 
275 		add_history(ip, hist, elm);
276 		if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID |
277 				        HAMMER_IOC_HISTORY_NEXT_KEY |
278 				        HAMMER_IOC_HISTORY_EOF)) {
279 			break;
280 		}
281 		error = hammer_btree_iterate(&cursor);
282 	}
283 	if (error == ENOENT) {
284 		hist->head.flags |= HAMMER_IOC_HISTORY_EOF;
285 		error = 0;
286 	}
287 	hammer_done_cursor(&cursor);
288 	return(error);
289 }
290 
291 /*
292  * Add the scanned element to the ioctl return structure.  Some special
293  * casing is required for regular files to accomodate how data ranges are
294  * stored on-disk.
295  */
296 static void
297 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
298 	    hammer_btree_elm_t elm)
299 {
300 	int i;
301 
302 	if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD)
303 		return;
304 	if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) &&
305 	    ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) {
306 		/*
307 		 * Adjust nxt_key
308 		 */
309 		if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len &&
310 		    hist->key < elm->leaf.base.key - elm->leaf.data_len) {
311 			hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len;
312 		}
313 		if (hist->nxt_key > elm->leaf.base.key)
314 			hist->nxt_key = elm->leaf.base.key;
315 
316 		/*
317 		 * Record is beyond MAXPHYS, there won't be any more records
318 		 * in the iteration covering the requested offset (key).
319 		 */
320 		if (elm->leaf.base.key >= MAXPHYS &&
321 		    elm->leaf.base.key - MAXPHYS > hist->key) {
322 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
323 		}
324 
325 		/*
326 		 * Data-range of record does not cover the key.
327 		 */
328 		if (elm->leaf.base.key - elm->leaf.data_len > hist->key)
329 			return;
330 
331 	} else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
332 		/*
333 		 * Adjust nxt_key
334 		 */
335 		if (hist->nxt_key > elm->leaf.base.key &&
336 		    hist->key < elm->leaf.base.key) {
337 			hist->nxt_key = elm->leaf.base.key;
338 		}
339 
340 		/*
341 		 * Record is beyond the requested key.
342 		 */
343 		if (elm->leaf.base.key > hist->key)
344 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
345 	}
346 
347 	/*
348 	 * Add create_tid if it is in-bounds.
349 	 */
350 	i = hist->count;
351 	if ((i == 0 ||
352 	     elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) &&
353 	    elm->leaf.base.create_tid >= hist->beg_tid &&
354 	    elm->leaf.base.create_tid < hist->end_tid) {
355 		if (hist->count == HAMMER_MAX_HISTORY_ELMS) {
356 			hist->nxt_tid = elm->leaf.base.create_tid;
357 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
358 			return;
359 		}
360 		hist->hist_ary[i].tid = elm->leaf.base.create_tid;
361 		hist->hist_ary[i].time32 = elm->leaf.create_ts;
362 		++hist->count;
363 	}
364 
365 	/*
366 	 * Add delete_tid if it is in-bounds.  Note that different portions
367 	 * of the history may have overlapping data ranges with different
368 	 * delete_tid's.  If this case occurs the delete_tid may match the
369 	 * create_tid of a following record.  XXX
370 	 *
371 	 *	[        ]
372 	 *            [     ]
373 	 */
374 	i = hist->count;
375 	if (elm->leaf.base.delete_tid &&
376 	    elm->leaf.base.delete_tid >= hist->beg_tid &&
377 	    elm->leaf.base.delete_tid < hist->end_tid) {
378 		if (i == HAMMER_MAX_HISTORY_ELMS) {
379 			hist->nxt_tid = elm->leaf.base.delete_tid;
380 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
381 			return;
382 		}
383 		hist->hist_ary[i].tid = elm->leaf.base.delete_tid;
384 		hist->hist_ary[i].time32 = elm->leaf.delete_ts;
385 		++hist->count;
386 	}
387 }
388 
389 /*
390  * Acquire synchronization TID
391  */
392 static
393 int
394 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip,
395 		   struct hammer_ioc_synctid *std)
396 {
397 	hammer_mount_t hmp = ip->hmp;
398 	int error = 0;
399 
400 	switch(std->op) {
401 	case HAMMER_SYNCTID_NONE:
402 		std->tid = hmp->flusher.tid;	/* inaccurate */
403 		break;
404 	case HAMMER_SYNCTID_ASYNC:
405 		hammer_queue_inodes_flusher(hmp, MNT_NOWAIT);
406 		hammer_flusher_async(hmp, NULL);
407 		std->tid = hmp->flusher.tid;	/* inaccurate */
408 		break;
409 	case HAMMER_SYNCTID_SYNC1:
410 		hammer_queue_inodes_flusher(hmp, MNT_WAIT);
411 		hammer_flusher_sync(hmp);
412 		std->tid = hmp->flusher.tid;
413 		break;
414 	case HAMMER_SYNCTID_SYNC2:
415 		hammer_queue_inodes_flusher(hmp, MNT_WAIT);
416 		hammer_flusher_sync(hmp);
417 		std->tid = hmp->flusher.tid;
418 		hammer_flusher_sync(hmp);
419 		break;
420 	default:
421 		error = EOPNOTSUPP;
422 		break;
423 	}
424 	return(error);
425 }
426 
427 /*
428  * Retrieve version info.
429  *
430  * Load min_version, wip_version, and max_versino.  If cur_version is passed
431  * as 0 then load the current version into cur_version.  Load the description
432  * for cur_version into the description array.
433  *
434  * Returns 0 on success, EINVAL if cur_version is non-zero and set to an
435  * unsupported value.
436  */
437 static
438 int
439 hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip,
440 		   struct hammer_ioc_version *ver)
441 {
442 	int error = 0;
443 
444 	ver->min_version = HAMMER_VOL_VERSION_MIN;
445 	ver->wip_version = HAMMER_VOL_VERSION_WIP;
446 	ver->max_version = HAMMER_VOL_VERSION_MAX;
447 	if (ver->cur_version == 0)
448 		ver->cur_version = trans->hmp->version;
449 	switch(ver->cur_version) {
450 	case 1:
451 		ksnprintf(ver->description, sizeof(ver->description),
452 			 "2.0 - First HAMMER release");
453 		break;
454 	case 2:
455 		ksnprintf(ver->description, sizeof(ver->description),
456 			 "2.3 - New directory entry layout");
457 		break;
458 	default:
459 		ksnprintf(ver->description, sizeof(ver->description),
460 			 "Unknown");
461 		error = EINVAL;
462 		break;
463 	}
464 	return(error);
465 };
466 
467 /*
468  * Set version info
469  */
470 static
471 int
472 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip,
473 		   struct hammer_ioc_version *ver)
474 {
475 	struct hammer_cursor cursor;
476 	hammer_volume_t volume;
477 	int error;
478 
479 	if (ver->cur_version < trans->hmp->version)
480 		return(EINVAL);
481 	if (ver->cur_version == trans->hmp->version)
482 		return(0);
483 	if (ver->cur_version > HAMMER_VOL_VERSION_MAX)
484 		return(EINVAL);
485 	if (trans->hmp->ronly)
486 		return(EROFS);
487 
488 	/*
489 	 * Update the root volume header and the version cached in
490 	 * the hammer_mount structure.
491 	 */
492 	error = hammer_init_cursor(trans, &cursor, NULL, NULL);
493 	if (error)
494 		goto failed;
495 	hammer_sync_lock_sh(trans);
496 
497 	volume = hammer_get_root_volume(cursor.trans->hmp, &error);
498 	KKASSERT(error == 0);
499 	hammer_modify_volume_field(cursor.trans, volume, vol_version);
500 	volume->ondisk->vol_version = ver->cur_version;
501 	cursor.trans->hmp->version = ver->cur_version;
502 	hammer_modify_volume_done(volume);
503 	hammer_rel_volume(volume, 0);
504 
505 	hammer_sync_unlock(trans);
506 failed:
507 	ver->head.error = error;
508 	hammer_done_cursor(&cursor);
509 	return(0);
510 }
511 
512 /*
513  * Get information
514  */
515 static
516 int
517 hammer_ioc_get_info(hammer_transaction_t trans, struct hammer_ioc_info *info) {
518 
519 	struct hammer_volume_ondisk	*od = trans->hmp->rootvol->ondisk;
520 	struct hammer_mount 		*hm = trans->hmp;
521 
522 	/* Fill the structure with the necessary information */
523 	_hammer_checkspace(hm, HAMMER_CHKSPC_WRITE, &info->rsvbigblocks);
524 	info->rsvbigblocks = info->rsvbigblocks >> HAMMER_LARGEBLOCK_BITS;
525 	strlcpy(info->vol_name, od->vol_name, sizeof(od->vol_name));
526 
527 	info->vol_fsid = hm->fsid;
528 	info->vol_fstype = od->vol_fstype;
529 	info->version = hm->version;
530 
531 	info->inodes = od->vol0_stat_inodes;
532 	info->bigblocks = od->vol0_stat_bigblocks;
533 	info->freebigblocks = od->vol0_stat_freebigblocks;
534 	info->nvolumes = hm->nvolumes;
535 
536 	return 0;
537 }
538 
539