Merge branch 'vendor/LESS'
[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                         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