Merge branch 'master' of git://venus/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_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