xref: /dflybsd-src/sys/vfs/hammer/hammer_ioctl.c (revision d54592ee9e96c920b951af2e00cd72c0081ccae3)
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.30 2008/07/31 04:42:04 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 
44 int
45 hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
46 	     struct ucred *cred)
47 {
48 	struct hammer_transaction trans;
49 	int error;
50 
51 	error = suser_cred(cred, PRISON_ROOT);
52 
53 	hammer_start_transaction(&trans, ip->hmp);
54 
55 	switch(com) {
56 	case HAMMERIOC_PRUNE:
57 		if (error == 0) {
58 			error = hammer_ioc_prune(&trans, ip,
59 					(struct hammer_ioc_prune *)data);
60 		}
61 		break;
62 	case HAMMERIOC_GETHISTORY:
63 		error = hammer_ioc_gethistory(&trans, ip,
64 					(struct hammer_ioc_history *)data);
65 		break;
66 	case HAMMERIOC_REBLOCK:
67 		if (error == 0) {
68 			error = hammer_ioc_reblock(&trans, ip,
69 					(struct hammer_ioc_reblock *)data);
70 		}
71 		break;
72 	case HAMMERIOC_SYNCTID:
73 		error = hammer_ioc_synctid(&trans, ip,
74 					(struct hammer_ioc_synctid *)data);
75 		break;
76 	case HAMMERIOC_GET_PSEUDOFS:
77 		error = hammer_ioc_get_pseudofs(&trans, ip,
78 				    (struct hammer_ioc_pseudofs_rw *)data);
79 		break;
80 	case HAMMERIOC_SET_PSEUDOFS:
81 		if (error == 0) {
82 			error = hammer_ioc_set_pseudofs(&trans, ip, cred,
83 				    (struct hammer_ioc_pseudofs_rw *)data);
84 		}
85 		break;
86 	case HAMMERIOC_UPG_PSEUDOFS:
87 		if (error == 0) {
88 			error = hammer_ioc_upgrade_pseudofs(&trans, ip,
89 				    (struct hammer_ioc_pseudofs_rw *)data);
90 		}
91 		break;
92 	case HAMMERIOC_DGD_PSEUDOFS:
93 		if (error == 0) {
94 			error = hammer_ioc_downgrade_pseudofs(&trans, ip,
95 				    (struct hammer_ioc_pseudofs_rw *)data);
96 		}
97 		break;
98 	case HAMMERIOC_RMR_PSEUDOFS:
99 		if (error == 0) {
100 			error = hammer_ioc_destroy_pseudofs(&trans, ip,
101 				    (struct hammer_ioc_pseudofs_rw *)data);
102 		}
103 		break;
104 	case HAMMERIOC_WAI_PSEUDOFS:
105 		if (error == 0) {
106 			error = hammer_ioc_wait_pseudofs(&trans, ip,
107 				    (struct hammer_ioc_pseudofs_rw *)data);
108 		}
109 		break;
110 	case HAMMERIOC_MIRROR_READ:
111 		if (error == 0) {
112 			error = hammer_ioc_mirror_read(&trans, ip,
113 				    (struct hammer_ioc_mirror_rw *)data);
114 		}
115 		break;
116 	case HAMMERIOC_MIRROR_WRITE:
117 		if (error == 0) {
118 			error = hammer_ioc_mirror_write(&trans, ip,
119 				    (struct hammer_ioc_mirror_rw *)data);
120 		}
121 		break;
122 	default:
123 		error = EOPNOTSUPP;
124 		break;
125 	}
126 	hammer_done_transaction(&trans);
127 	return (error);
128 }
129 
130 /*
131  * Iterate through an object's inode or an object's records and record
132  * modification TIDs.
133  */
134 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
135 			hammer_btree_elm_t elm);
136 
137 static
138 int
139 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip,
140 		      struct hammer_ioc_history *hist)
141 {
142 	struct hammer_cursor cursor;
143 	hammer_btree_elm_t elm;
144 	int error;
145 
146 	/*
147 	 * Validate the structure and initialize for return.
148 	 */
149 	if (hist->beg_tid > hist->end_tid)
150 		return(EINVAL);
151 	if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
152 		if (hist->key > hist->nxt_key)
153 			return(EINVAL);
154 	}
155 
156 	hist->obj_id = ip->obj_id;
157 	hist->count = 0;
158 	hist->nxt_tid = hist->end_tid;
159 	hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID;
160 	hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY;
161 	hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF;
162 	hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED;
163 	if ((ip->flags & HAMMER_INODE_MODMASK) &
164 	    ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) {
165 		hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED;
166 	}
167 
168 	/*
169 	 * Setup the cursor.  We can't handle undeletable records
170 	 * (create_tid of 0) at the moment.  A create_tid of 0 has
171 	 * a special meaning and cannot be specified in the cursor.
172 	 */
173 	error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
174 	if (error) {
175 		hammer_done_cursor(&cursor);
176 		return(error);
177 	}
178 
179 	cursor.key_beg.obj_id = hist->obj_id;
180 	cursor.key_beg.create_tid = hist->beg_tid;
181 	cursor.key_beg.delete_tid = 0;
182 	cursor.key_beg.obj_type = 0;
183 	if (cursor.key_beg.create_tid == HAMMER_MIN_TID)
184 		cursor.key_beg.create_tid = 1;
185 
186 	cursor.key_end.obj_id = hist->obj_id;
187 	cursor.key_end.create_tid = hist->end_tid;
188 	cursor.key_end.delete_tid = 0;
189 	cursor.key_end.obj_type = 0;
190 
191 	cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE;
192 
193 	if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
194 		/*
195 		 * key-range within the file.  For a regular file the
196 		 * on-disk key represents BASE+LEN, not BASE, so the
197 		 * first possible record containing the offset 'key'
198 		 * has an on-disk key of (key + 1).
199 		 */
200 		cursor.key_beg.key = hist->key;
201 		cursor.key_end.key = HAMMER_MAX_KEY;
202 		cursor.key_beg.localization = ip->obj_localization +
203 					      HAMMER_LOCALIZE_MISC;
204 		cursor.key_end.localization = ip->obj_localization +
205 					      HAMMER_LOCALIZE_MISC;
206 
207 		switch(ip->ino_data.obj_type) {
208 		case HAMMER_OBJTYPE_REGFILE:
209 			++cursor.key_beg.key;
210 			cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
211 			break;
212 		case HAMMER_OBJTYPE_DIRECTORY:
213 			cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
214 			break;
215 		case HAMMER_OBJTYPE_DBFILE:
216 			cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
217 			break;
218 		default:
219 			error = EINVAL;
220 			break;
221 		}
222 		cursor.key_end.rec_type = cursor.key_beg.rec_type;
223 	} else {
224 		/*
225 		 * The inode itself.
226 		 */
227 		cursor.key_beg.key = 0;
228 		cursor.key_end.key = 0;
229 		cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
230 		cursor.key_end.rec_type = HAMMER_RECTYPE_INODE;
231 		cursor.key_beg.localization = ip->obj_localization +
232 					      HAMMER_LOCALIZE_INODE;
233 		cursor.key_end.localization = ip->obj_localization +
234 					      HAMMER_LOCALIZE_INODE;
235 	}
236 
237 	error = hammer_btree_first(&cursor);
238 	while (error == 0) {
239 		elm = &cursor.node->ondisk->elms[cursor.index];
240 
241 		add_history(ip, hist, elm);
242 		if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID |
243 				        HAMMER_IOC_HISTORY_NEXT_KEY |
244 				        HAMMER_IOC_HISTORY_EOF)) {
245 			break;
246 		}
247 		error = hammer_btree_iterate(&cursor);
248 	}
249 	if (error == ENOENT) {
250 		hist->head.flags |= HAMMER_IOC_HISTORY_EOF;
251 		error = 0;
252 	}
253 	hammer_done_cursor(&cursor);
254 	return(error);
255 }
256 
257 /*
258  * Add the scanned element to the ioctl return structure.  Some special
259  * casing is required for regular files to accomodate how data ranges are
260  * stored on-disk.
261  */
262 static void
263 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
264 	    hammer_btree_elm_t elm)
265 {
266 	int i;
267 
268 	if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD)
269 		return;
270 	if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) &&
271 	    ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) {
272 		/*
273 		 * Adjust nxt_key
274 		 */
275 		if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len &&
276 		    hist->key < elm->leaf.base.key - elm->leaf.data_len) {
277 			hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len;
278 		}
279 		if (hist->nxt_key > elm->leaf.base.key)
280 			hist->nxt_key = elm->leaf.base.key;
281 
282 		/*
283 		 * Record is beyond MAXPHYS, there won't be any more records
284 		 * in the iteration covering the requested offset (key).
285 		 */
286 		if (elm->leaf.base.key >= MAXPHYS &&
287 		    elm->leaf.base.key - MAXPHYS > hist->key) {
288 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
289 		}
290 
291 		/*
292 		 * Data-range of record does not cover the key.
293 		 */
294 		if (elm->leaf.base.key - elm->leaf.data_len > hist->key)
295 			return;
296 
297 	} else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
298 		/*
299 		 * Adjust nxt_key
300 		 */
301 		if (hist->nxt_key > elm->leaf.base.key &&
302 		    hist->key < elm->leaf.base.key) {
303 			hist->nxt_key = elm->leaf.base.key;
304 		}
305 
306 		/*
307 		 * Record is beyond the requested key.
308 		 */
309 		if (elm->leaf.base.key > hist->key)
310 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
311 	}
312 
313 	/*
314 	 * Add create_tid if it is in-bounds.
315 	 */
316 	i = hist->count;
317 	if ((i == 0 ||
318 	     elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) &&
319 	    elm->leaf.base.create_tid >= hist->beg_tid &&
320 	    elm->leaf.base.create_tid < hist->end_tid) {
321 		if (hist->count == HAMMER_MAX_HISTORY_ELMS) {
322 			hist->nxt_tid = elm->leaf.base.create_tid;
323 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
324 			return;
325 		}
326 		hist->hist_ary[i].tid = elm->leaf.base.create_tid;
327 		hist->hist_ary[i].time32 = elm->leaf.create_ts;
328 		++hist->count;
329 	}
330 
331 	/*
332 	 * Add delete_tid if it is in-bounds.  Note that different portions
333 	 * of the history may have overlapping data ranges with different
334 	 * delete_tid's.  If this case occurs the delete_tid may match the
335 	 * create_tid of a following record.  XXX
336 	 *
337 	 *	[        ]
338 	 *            [     ]
339 	 */
340 	i = hist->count;
341 	if (elm->leaf.base.delete_tid &&
342 	    elm->leaf.base.delete_tid >= hist->beg_tid &&
343 	    elm->leaf.base.delete_tid < hist->end_tid) {
344 		if (i == HAMMER_MAX_HISTORY_ELMS) {
345 			hist->nxt_tid = elm->leaf.base.delete_tid;
346 			hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
347 			return;
348 		}
349 		hist->hist_ary[i].tid = elm->leaf.base.delete_tid;
350 		hist->hist_ary[i].time32 = elm->leaf.delete_ts;
351 		++hist->count;
352 	}
353 }
354 
355 /*
356  * Acquire synchronization TID
357  */
358 static
359 int
360 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip,
361 		   struct hammer_ioc_synctid *std)
362 {
363 	hammer_mount_t hmp = ip->hmp;
364 	int error = 0;
365 
366 	switch(std->op) {
367 	case HAMMER_SYNCTID_NONE:
368 		std->tid = hmp->flusher.tid;	/* inaccurate */
369 		break;
370 	case HAMMER_SYNCTID_ASYNC:
371 		hammer_queue_inodes_flusher(hmp, MNT_NOWAIT);
372 		hammer_flusher_async(hmp, NULL);
373 		std->tid = hmp->flusher.tid;	/* inaccurate */
374 		break;
375 	case HAMMER_SYNCTID_SYNC1:
376 		hammer_queue_inodes_flusher(hmp, MNT_WAIT);
377 		hammer_flusher_sync(hmp);
378 		std->tid = hmp->flusher.tid;
379 		break;
380 	case HAMMER_SYNCTID_SYNC2:
381 		hammer_queue_inodes_flusher(hmp, MNT_WAIT);
382 		hammer_flusher_sync(hmp);
383 		std->tid = hmp->flusher.tid;
384 		hammer_flusher_sync(hmp);
385 		break;
386 	default:
387 		error = EOPNOTSUPP;
388 		break;
389 	}
390 	return(error);
391 }
392 
393