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