xref: /dragonfly/sys/vfs/hammer/hammer_ioctl.c (revision c541a65d)
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_SYNCTID:
79 		error = hammer_ioc_synctid(&trans, ip,
80 					(struct hammer_ioc_synctid *)data);
81 		break;
82 	case HAMMERIOC_GET_PSEUDOFS:
83 		error = hammer_ioc_get_pseudofs(&trans, ip,
84 				    (struct hammer_ioc_pseudofs_rw *)data);
85 		break;
86 	case HAMMERIOC_SET_PSEUDOFS:
87 		if (error == 0) {
88 			error = hammer_ioc_set_pseudofs(&trans, ip, cred,
89 				    (struct hammer_ioc_pseudofs_rw *)data);
90 		}
91 		break;
92 	case HAMMERIOC_UPG_PSEUDOFS:
93 		if (error == 0) {
94 			error = hammer_ioc_upgrade_pseudofs(&trans, ip,
95 				    (struct hammer_ioc_pseudofs_rw *)data);
96 		}
97 		break;
98 	case HAMMERIOC_DGD_PSEUDOFS:
99 		if (error == 0) {
100 			error = hammer_ioc_downgrade_pseudofs(&trans, ip,
101 				    (struct hammer_ioc_pseudofs_rw *)data);
102 		}
103 		break;
104 	case HAMMERIOC_RMR_PSEUDOFS:
105 		if (error == 0) {
106 			error = hammer_ioc_destroy_pseudofs(&trans, ip,
107 				    (struct hammer_ioc_pseudofs_rw *)data);
108 		}
109 		break;
110 	case HAMMERIOC_WAI_PSEUDOFS:
111 		if (error == 0) {
112 			error = hammer_ioc_wait_pseudofs(&trans, ip,
113 				    (struct hammer_ioc_pseudofs_rw *)data);
114 		}
115 		break;
116 	case HAMMERIOC_MIRROR_READ:
117 		if (error == 0) {
118 			error = hammer_ioc_mirror_read(&trans, ip,
119 				    (struct hammer_ioc_mirror_rw *)data);
120 		}
121 		break;
122 	case HAMMERIOC_MIRROR_WRITE:
123 		if (error == 0) {
124 			error = hammer_ioc_mirror_write(&trans, ip,
125 				    (struct hammer_ioc_mirror_rw *)data);
126 		}
127 		break;
128 	case HAMMERIOC_GET_VERSION:
129 		error = hammer_ioc_get_version(&trans, ip,
130 				    (struct hammer_ioc_version *)data);
131 		break;
132 	case HAMMERIOC_SET_VERSION:
133 		if (error == 0) {
134 			error = hammer_ioc_set_version(&trans, ip,
135 					    (struct hammer_ioc_version *)data);
136 		}
137 		break;
138 	default:
139 		error = EOPNOTSUPP;
140 		break;
141 	}
142 	hammer_done_transaction(&trans);
143 	return (error);
144 }
145 
146 /*
147  * Iterate through an object's inode or an object's records and record
148  * modification TIDs.
149  */
150 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
151 			hammer_btree_elm_t elm);
152 
153 static
154 int
155 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip,
156 		      struct hammer_ioc_history *hist)
157 {
158 	struct hammer_cursor cursor;
159 	hammer_btree_elm_t elm;
160 	int error;
161 
162 	/*
163 	 * Validate the structure and initialize for return.
164 	 */
165 	if (hist->beg_tid > hist->end_tid)
166 		return(EINVAL);
167 	if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
168 		if (hist->key > hist->nxt_key)
169 			return(EINVAL);
170 	}
171 
172 	hist->obj_id = ip->obj_id;
173 	hist->count = 0;
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;
182 	}
183 
184 	/*
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.
188 	 */
189 	error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
190 	if (error) {
191 		hammer_done_cursor(&cursor);
192 		return(error);
193 	}
194 
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;
201 
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;
206 
207 	cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE;
208 
209 	if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
210 		/*
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).
215 		 */
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;
222 
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;
227 			break;
228 		case HAMMER_OBJTYPE_DIRECTORY:
229 			cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
230 			break;
231 		case HAMMER_OBJTYPE_DBFILE:
232 			cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
233 			break;
234 		default:
235 			error = EINVAL;
236 			break;
237 		}
238 		cursor.key_end.rec_type = cursor.key_beg.rec_type;
239 	} else {
240 		/*
241 		 * The inode itself.
242 		 */
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;
251 	}
252 
253 	error = hammer_btree_first(&cursor);
254 	while (error == 0) {
255 		elm = &cursor.node->ondisk->elms[cursor.index];
256 
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)) {
261 			break;
262 		}
263 		error = hammer_btree_iterate(&cursor);
264 	}
265 	if (error == ENOENT) {
266 		hist->head.flags |= HAMMER_IOC_HISTORY_EOF;
267 		error = 0;
268 	}
269 	hammer_done_cursor(&cursor);
270 	return(error);
271 }
272 
273 /*
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
276  * stored on-disk.
277  */
278 static void
279 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
280 	    hammer_btree_elm_t elm)
281 {
282 	int i;
283 
284 	if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD)
285 		return;
286 	if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) &&
287 	    ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) {
288 		/*
289 		 * Adjust nxt_key
290 		 */
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;
294 		}
295 		if (hist->nxt_key > elm->leaf.base.key)
296 			hist->nxt_key = elm->leaf.base.key;
297 
298 		/*
299 		 * Record is beyond MAXPHYS, there won't be any more records
300 		 * in the iteration covering the requested offset (key).
301 		 */
302 		if (elm->leaf.base.key >= MAXPHYS &&
303 		    elm->leaf.base.key - MAXPHYS > hist->key) {
304 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
305 		}
306 
307 		/*
308 		 * Data-range of record does not cover the key.
309 		 */
310 		if (elm->leaf.base.key - elm->leaf.data_len > hist->key)
311 			return;
312 
313 	} else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
314 		/*
315 		 * Adjust nxt_key
316 		 */
317 		if (hist->nxt_key > elm->leaf.base.key &&
318 		    hist->key < elm->leaf.base.key) {
319 			hist->nxt_key = elm->leaf.base.key;
320 		}
321 
322 		/*
323 		 * Record is beyond the requested key.
324 		 */
325 		if (elm->leaf.base.key > hist->key)
326 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
327 	}
328 
329 	/*
330 	 * Add create_tid if it is in-bounds.
331 	 */
332 	i = hist->count;
333 	if ((i == 0 ||
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;
340 			return;
341 		}
342 		hist->hist_ary[i].tid = elm->leaf.base.create_tid;
343 		hist->hist_ary[i].time32 = elm->leaf.create_ts;
344 		++hist->count;
345 	}
346 
347 	/*
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
352 	 *
353 	 *	[        ]
354 	 *            [     ]
355 	 */
356 	i = hist->count;
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;
363 			return;
364 		}
365 		hist->hist_ary[i].tid = elm->leaf.base.delete_tid;
366 		hist->hist_ary[i].time32 = elm->leaf.delete_ts;
367 		++hist->count;
368 	}
369 }
370 
371 /*
372  * Acquire synchronization TID
373  */
374 static
375 int
376 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip,
377 		   struct hammer_ioc_synctid *std)
378 {
379 	hammer_mount_t hmp = ip->hmp;
380 	int error = 0;
381 
382 	switch(std->op) {
383 	case HAMMER_SYNCTID_NONE:
384 		std->tid = hmp->flusher.tid;	/* inaccurate */
385 		break;
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 */
390 		break;
391 	case HAMMER_SYNCTID_SYNC1:
392 		hammer_queue_inodes_flusher(hmp, MNT_WAIT);
393 		hammer_flusher_sync(hmp);
394 		std->tid = hmp->flusher.tid;
395 		break;
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);
401 		break;
402 	default:
403 		error = EOPNOTSUPP;
404 		break;
405 	}
406 	return(error);
407 }
408 
409 /*
410  * Retrieve version info.
411  *
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.
415  *
416  * Returns 0 on success, EINVAL if cur_version is non-zero and set to an
417  * unsupported value.
418  */
419 static
420 int
421 hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip,
422 		   struct hammer_ioc_version *ver)
423 {
424 	int error = 0;
425 
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) {
432 	case 1:
433 		ksnprintf(ver->description, sizeof(ver->description),
434 			 "2.0 - First HAMMER release");
435 		break;
436 	case 2:
437 		ksnprintf(ver->description, sizeof(ver->description),
438 			 "2.2 - New directory hash");
439 		break;
440 	default:
441 		ksnprintf(ver->description, sizeof(ver->description),
442 			 "Unknown");
443 		error = EINVAL;
444 		break;
445 	}
446 	return(error);
447 };
448 
449 /*
450  * Set version info
451  */
452 static
453 int
454 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip,
455 		   struct hammer_ioc_version *ver)
456 {
457 	struct hammer_cursor cursor;
458 	hammer_volume_t volume;
459 	int error;
460 
461 	if (ver->cur_version < trans->hmp->version)
462 		return(EINVAL);
463 	if (ver->cur_version == trans->hmp->version)
464 		return(0);
465 	if (ver->cur_version > HAMMER_VOL_VERSION_MAX)
466 		return(EINVAL);
467 	if (trans->hmp->ronly)
468 		return(EROFS);
469 
470 	/*
471 	 * Update the root volume header and the version cached in
472 	 * the hammer_mount structure.
473 	 */
474 	error = hammer_init_cursor(trans, &cursor, NULL, NULL);
475 	if (error)
476 		goto failed;
477 	hammer_sync_lock_sh(trans);
478 
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);
486 
487 	hammer_sync_unlock(trans);
488 failed:
489 	ver->head.error = error;
490 	hammer_done_cursor(&cursor);
491 	return(0);
492 }
493 
494 
495 
496