Merge branch 'master' of ssh://crater.dragonflybsd.org/repository/git/dragonfly
[dragonfly.git] / sys / vfs / hammer / hammer_ioctl.c
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                         cursor.key_beg.localization =
237                                                 hammer_dir_localization(ip);
238                         cursor.key_end.localization =
239                                                 hammer_dir_localization(ip);
240                         break;
241                 case HAMMER_OBJTYPE_DBFILE:
242                         cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
243                         break;
244                 default:
245                         error = EINVAL;
246                         break;
247                 }
248                 cursor.key_end.rec_type = cursor.key_beg.rec_type;
249         } else {
250                 /*
251                  * The inode itself.
252                  */
253                 cursor.key_beg.key = 0;
254                 cursor.key_end.key = 0;
255                 cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
256                 cursor.key_end.rec_type = HAMMER_RECTYPE_INODE;
257                 cursor.key_beg.localization = ip->obj_localization +
258                                               HAMMER_LOCALIZE_INODE;
259                 cursor.key_end.localization = ip->obj_localization +
260                                               HAMMER_LOCALIZE_INODE;
261         }
262
263         error = hammer_btree_first(&cursor);
264         while (error == 0) {
265                 elm = &cursor.node->ondisk->elms[cursor.index];
266
267                 add_history(ip, hist, elm);
268                 if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID |
269                                         HAMMER_IOC_HISTORY_NEXT_KEY |
270                                         HAMMER_IOC_HISTORY_EOF)) {
271                         break;
272                 }
273                 error = hammer_btree_iterate(&cursor);
274         }
275         if (error == ENOENT) {
276                 hist->head.flags |= HAMMER_IOC_HISTORY_EOF;
277                 error = 0;
278         }
279         hammer_done_cursor(&cursor);
280         return(error);
281 }
282
283 /*
284  * Add the scanned element to the ioctl return structure.  Some special
285  * casing is required for regular files to accomodate how data ranges are
286  * stored on-disk.
287  */
288 static void
289 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
290             hammer_btree_elm_t elm)
291 {
292         int i;
293
294         if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD)
295                 return;
296         if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) &&
297             ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) {
298                 /*
299                  * Adjust nxt_key
300                  */
301                 if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len &&
302                     hist->key < elm->leaf.base.key - elm->leaf.data_len) {
303                         hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len;
304                 }
305                 if (hist->nxt_key > elm->leaf.base.key)
306                         hist->nxt_key = elm->leaf.base.key;
307
308                 /*
309                  * Record is beyond MAXPHYS, there won't be any more records
310                  * in the iteration covering the requested offset (key).
311                  */
312                 if (elm->leaf.base.key >= MAXPHYS &&
313                     elm->leaf.base.key - MAXPHYS > hist->key) {
314                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
315                 }
316
317                 /*
318                  * Data-range of record does not cover the key.
319                  */
320                 if (elm->leaf.base.key - elm->leaf.data_len > hist->key)
321                         return;
322
323         } else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
324                 /*
325                  * Adjust nxt_key
326                  */
327                 if (hist->nxt_key > elm->leaf.base.key &&
328                     hist->key < elm->leaf.base.key) {
329                         hist->nxt_key = elm->leaf.base.key;
330                 }
331
332                 /*
333                  * Record is beyond the requested key.
334                  */
335                 if (elm->leaf.base.key > hist->key)
336                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
337         }
338
339         /*
340          * Add create_tid if it is in-bounds.
341          */
342         i = hist->count;
343         if ((i == 0 ||
344              elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) &&
345             elm->leaf.base.create_tid >= hist->beg_tid &&
346             elm->leaf.base.create_tid < hist->end_tid) {
347                 if (hist->count == HAMMER_MAX_HISTORY_ELMS) {
348                         hist->nxt_tid = elm->leaf.base.create_tid;
349                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
350                         return;
351                 }
352                 hist->hist_ary[i].tid = elm->leaf.base.create_tid;
353                 hist->hist_ary[i].time32 = elm->leaf.create_ts;
354                 ++hist->count;
355         }
356
357         /*
358          * Add delete_tid if it is in-bounds.  Note that different portions
359          * of the history may have overlapping data ranges with different
360          * delete_tid's.  If this case occurs the delete_tid may match the
361          * create_tid of a following record.  XXX
362          *
363          *      [        ]
364          *            [     ]
365          */
366         i = hist->count;
367         if (elm->leaf.base.delete_tid &&
368             elm->leaf.base.delete_tid >= hist->beg_tid &&
369             elm->leaf.base.delete_tid < hist->end_tid) {
370                 if (i == HAMMER_MAX_HISTORY_ELMS) {
371                         hist->nxt_tid = elm->leaf.base.delete_tid;
372                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
373                         return;
374                 }
375                 hist->hist_ary[i].tid = elm->leaf.base.delete_tid;
376                 hist->hist_ary[i].time32 = elm->leaf.delete_ts;
377                 ++hist->count;
378         }
379 }
380
381 /*
382  * Acquire synchronization TID
383  */
384 static
385 int
386 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip,
387                    struct hammer_ioc_synctid *std)
388 {
389         hammer_mount_t hmp = ip->hmp;
390         int error = 0;
391
392         switch(std->op) {
393         case HAMMER_SYNCTID_NONE:
394                 std->tid = hmp->flusher.tid;    /* inaccurate */
395                 break;
396         case HAMMER_SYNCTID_ASYNC:
397                 hammer_queue_inodes_flusher(hmp, MNT_NOWAIT);
398                 hammer_flusher_async(hmp, NULL);
399                 std->tid = hmp->flusher.tid;    /* inaccurate */
400                 break;
401         case HAMMER_SYNCTID_SYNC1:
402                 hammer_queue_inodes_flusher(hmp, MNT_WAIT);
403                 hammer_flusher_sync(hmp);
404                 std->tid = hmp->flusher.tid;
405                 break;
406         case HAMMER_SYNCTID_SYNC2:
407                 hammer_queue_inodes_flusher(hmp, MNT_WAIT);
408                 hammer_flusher_sync(hmp);
409                 std->tid = hmp->flusher.tid;
410                 hammer_flusher_sync(hmp);
411                 break;
412         default:
413                 error = EOPNOTSUPP;
414                 break;
415         }
416         return(error);
417 }
418
419 /*
420  * Retrieve version info.
421  *
422  * Load min_version, wip_version, and max_versino.  If cur_version is passed
423  * as 0 then load the current version into cur_version.  Load the description
424  * for cur_version into the description array.
425  *
426  * Returns 0 on success, EINVAL if cur_version is non-zero and set to an
427  * unsupported value.
428  */
429 static
430 int
431 hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip,
432                    struct hammer_ioc_version *ver)
433 {
434         int error = 0;
435
436         ver->min_version = HAMMER_VOL_VERSION_MIN;
437         ver->wip_version = HAMMER_VOL_VERSION_WIP;
438         ver->max_version = HAMMER_VOL_VERSION_MAX;
439         if (ver->cur_version == 0)
440                 ver->cur_version = trans->hmp->version;
441         switch(ver->cur_version) {
442         case 1:
443                 ksnprintf(ver->description, sizeof(ver->description),
444                          "2.0 - First HAMMER release");
445                 break;
446         case 2:
447                 ksnprintf(ver->description, sizeof(ver->description),
448                          "2.3 - New directory entry layout");
449                 break;
450         default:
451                 ksnprintf(ver->description, sizeof(ver->description),
452                          "Unknown");
453                 error = EINVAL;
454                 break;
455         }
456         return(error);
457 };
458
459 /*
460  * Set version info
461  */
462 static
463 int
464 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip,
465                    struct hammer_ioc_version *ver)
466 {
467         struct hammer_cursor cursor;
468         hammer_volume_t volume;
469         int error;
470
471         if (ver->cur_version < trans->hmp->version)
472                 return(EINVAL);
473         if (ver->cur_version == trans->hmp->version)
474                 return(0);
475         if (ver->cur_version > HAMMER_VOL_VERSION_MAX)
476                 return(EINVAL);
477         if (trans->hmp->ronly)
478                 return(EROFS);
479
480         /*
481          * Update the root volume header and the version cached in
482          * the hammer_mount structure.
483          */
484         error = hammer_init_cursor(trans, &cursor, NULL, NULL);
485         if (error)
486                 goto failed;
487         hammer_sync_lock_sh(trans);
488
489         volume = hammer_get_root_volume(cursor.trans->hmp, &error);
490         KKASSERT(error == 0);
491         hammer_modify_volume_field(cursor.trans, volume, vol_version);
492         volume->ondisk->vol_version = ver->cur_version;
493         cursor.trans->hmp->version = ver->cur_version;
494         hammer_modify_volume_done(volume);
495         hammer_rel_volume(volume, 0);
496
497         hammer_sync_unlock(trans);
498 failed:
499         ver->head.error = error;
500         hammer_done_cursor(&cursor);
501         return(0);
502 }
503
504
505