Merge branch 'vendor/GCC44' into gcc441
[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 static int hammer_ioc_get_info(hammer_transaction_t trans,
50                                 struct hammer_ioc_info *info);
51
52
53
54 int
55 hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
56              struct ucred *cred)
57 {
58         struct hammer_transaction trans;
59         int error;
60
61         error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0);
62
63         hammer_start_transaction(&trans, ip->hmp);
64
65         switch(com) {
66         case HAMMERIOC_PRUNE:
67                 if (error == 0) {
68                         error = hammer_ioc_prune(&trans, ip,
69                                         (struct hammer_ioc_prune *)data);
70                 }
71                 break;
72         case HAMMERIOC_GETHISTORY:
73                 error = hammer_ioc_gethistory(&trans, ip,
74                                         (struct hammer_ioc_history *)data);
75                 break;
76         case HAMMERIOC_REBLOCK:
77                 if (error == 0) {
78                         error = hammer_ioc_reblock(&trans, ip,
79                                         (struct hammer_ioc_reblock *)data);
80                 }
81                 break;
82         case HAMMERIOC_REBALANCE:
83                 if (error == 0) {
84                         error = hammer_ioc_rebalance(&trans, ip,
85                                         (struct hammer_ioc_rebalance *)data);
86                 }
87                 break;
88         case HAMMERIOC_SYNCTID:
89                 error = hammer_ioc_synctid(&trans, ip,
90                                         (struct hammer_ioc_synctid *)data);
91                 break;
92         case HAMMERIOC_GET_PSEUDOFS:
93                 error = hammer_ioc_get_pseudofs(&trans, ip,
94                                     (struct hammer_ioc_pseudofs_rw *)data);
95                 break;
96         case HAMMERIOC_SET_PSEUDOFS:
97                 if (error == 0) {
98                         error = hammer_ioc_set_pseudofs(&trans, ip, cred,
99                                     (struct hammer_ioc_pseudofs_rw *)data);
100                 }
101                 break;
102         case HAMMERIOC_UPG_PSEUDOFS:
103                 if (error == 0) {
104                         error = hammer_ioc_upgrade_pseudofs(&trans, ip, 
105                                     (struct hammer_ioc_pseudofs_rw *)data);
106                 }
107                 break;
108         case HAMMERIOC_DGD_PSEUDOFS:
109                 if (error == 0) {
110                         error = hammer_ioc_downgrade_pseudofs(&trans, ip,
111                                     (struct hammer_ioc_pseudofs_rw *)data);
112                 }
113                 break;
114         case HAMMERIOC_RMR_PSEUDOFS:
115                 if (error == 0) {
116                         error = hammer_ioc_destroy_pseudofs(&trans, ip,
117                                     (struct hammer_ioc_pseudofs_rw *)data);
118                 }
119                 break;
120         case HAMMERIOC_WAI_PSEUDOFS:
121                 if (error == 0) {
122                         error = hammer_ioc_wait_pseudofs(&trans, ip,
123                                     (struct hammer_ioc_pseudofs_rw *)data);
124                 }
125                 break;
126         case HAMMERIOC_MIRROR_READ:
127                 if (error == 0) {
128                         error = hammer_ioc_mirror_read(&trans, ip,
129                                     (struct hammer_ioc_mirror_rw *)data);
130                 }
131                 break;
132         case HAMMERIOC_MIRROR_WRITE:
133                 if (error == 0) {
134                         error = hammer_ioc_mirror_write(&trans, ip,
135                                     (struct hammer_ioc_mirror_rw *)data);
136                 }
137                 break;
138         case HAMMERIOC_GET_VERSION:
139                 error = hammer_ioc_get_version(&trans, ip, 
140                                     (struct hammer_ioc_version *)data);
141                 break;
142         case HAMMERIOC_GET_INFO:
143                 error = hammer_ioc_get_info(&trans,
144                                     (struct hammer_ioc_info *)data);
145                 break;
146         case HAMMERIOC_SET_VERSION:
147                 if (error == 0) {
148                         error = hammer_ioc_set_version(&trans, ip, 
149                                             (struct hammer_ioc_version *)data);
150                 }
151                 break;
152         case HAMMERIOC_EXPAND:
153                 if (error == 0) {
154                         error = hammer_ioc_expand(&trans, ip,
155                                             (struct hammer_ioc_expand *)data);
156                 }
157                 break;
158
159         default:
160                 error = EOPNOTSUPP;
161                 break;
162         }
163         hammer_done_transaction(&trans);
164         return (error);
165 }
166
167 /*
168  * Iterate through an object's inode or an object's records and record
169  * modification TIDs.
170  */
171 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
172                         hammer_btree_elm_t elm);
173
174 static
175 int
176 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip,
177                       struct hammer_ioc_history *hist)
178 {
179         struct hammer_cursor cursor;
180         hammer_btree_elm_t elm;
181         int error;
182
183         /*
184          * Validate the structure and initialize for return.
185          */
186         if (hist->beg_tid > hist->end_tid)
187                 return(EINVAL);
188         if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
189                 if (hist->key > hist->nxt_key)
190                         return(EINVAL);
191         }
192
193         hist->obj_id = ip->obj_id;
194         hist->count = 0;
195         hist->nxt_tid = hist->end_tid;
196         hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID;
197         hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY;
198         hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF;
199         hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED;
200         if ((ip->flags & HAMMER_INODE_MODMASK) & 
201             ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) {
202                 hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED;
203         }
204
205         /*
206          * Setup the cursor.  We can't handle undeletable records
207          * (create_tid of 0) at the moment.  A create_tid of 0 has
208          * a special meaning and cannot be specified in the cursor.
209          */
210         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
211         if (error) {
212                 hammer_done_cursor(&cursor);
213                 return(error);
214         }
215
216         cursor.key_beg.obj_id = hist->obj_id;
217         cursor.key_beg.create_tid = hist->beg_tid;
218         cursor.key_beg.delete_tid = 0;
219         cursor.key_beg.obj_type = 0;
220         if (cursor.key_beg.create_tid == HAMMER_MIN_TID)
221                 cursor.key_beg.create_tid = 1;
222
223         cursor.key_end.obj_id = hist->obj_id;
224         cursor.key_end.create_tid = hist->end_tid;
225         cursor.key_end.delete_tid = 0;
226         cursor.key_end.obj_type = 0;
227
228         cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE;
229
230         if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
231                 /*
232                  * key-range within the file.  For a regular file the
233                  * on-disk key represents BASE+LEN, not BASE, so the
234                  * first possible record containing the offset 'key'
235                  * has an on-disk key of (key + 1).
236                  */
237                 cursor.key_beg.key = hist->key;
238                 cursor.key_end.key = HAMMER_MAX_KEY;
239                 cursor.key_beg.localization = ip->obj_localization + 
240                                               HAMMER_LOCALIZE_MISC;
241                 cursor.key_end.localization = ip->obj_localization + 
242                                               HAMMER_LOCALIZE_MISC;
243
244                 switch(ip->ino_data.obj_type) {
245                 case HAMMER_OBJTYPE_REGFILE:
246                         ++cursor.key_beg.key;
247                         cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
248                         break;
249                 case HAMMER_OBJTYPE_DIRECTORY:
250                         cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
251                         cursor.key_beg.localization =
252                                                 hammer_dir_localization(ip);
253                         cursor.key_end.localization =
254                                                 hammer_dir_localization(ip);
255                         break;
256                 case HAMMER_OBJTYPE_DBFILE:
257                         cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
258                         break;
259                 default:
260                         error = EINVAL;
261                         break;
262                 }
263                 cursor.key_end.rec_type = cursor.key_beg.rec_type;
264         } else {
265                 /*
266                  * The inode itself.
267                  */
268                 cursor.key_beg.key = 0;
269                 cursor.key_end.key = 0;
270                 cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
271                 cursor.key_end.rec_type = HAMMER_RECTYPE_INODE;
272                 cursor.key_beg.localization = ip->obj_localization +
273                                               HAMMER_LOCALIZE_INODE;
274                 cursor.key_end.localization = ip->obj_localization +
275                                               HAMMER_LOCALIZE_INODE;
276         }
277
278         error = hammer_btree_first(&cursor);
279         while (error == 0) {
280                 elm = &cursor.node->ondisk->elms[cursor.index];
281
282                 add_history(ip, hist, elm);
283                 if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID |
284                                         HAMMER_IOC_HISTORY_NEXT_KEY |
285                                         HAMMER_IOC_HISTORY_EOF)) {
286                         break;
287                 }
288                 error = hammer_btree_iterate(&cursor);
289         }
290         if (error == ENOENT) {
291                 hist->head.flags |= HAMMER_IOC_HISTORY_EOF;
292                 error = 0;
293         }
294         hammer_done_cursor(&cursor);
295         return(error);
296 }
297
298 /*
299  * Add the scanned element to the ioctl return structure.  Some special
300  * casing is required for regular files to accomodate how data ranges are
301  * stored on-disk.
302  */
303 static void
304 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
305             hammer_btree_elm_t elm)
306 {
307         int i;
308
309         if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD)
310                 return;
311         if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) &&
312             ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) {
313                 /*
314                  * Adjust nxt_key
315                  */
316                 if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len &&
317                     hist->key < elm->leaf.base.key - elm->leaf.data_len) {
318                         hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len;
319                 }
320                 if (hist->nxt_key > elm->leaf.base.key)
321                         hist->nxt_key = elm->leaf.base.key;
322
323                 /*
324                  * Record is beyond MAXPHYS, there won't be any more records
325                  * in the iteration covering the requested offset (key).
326                  */
327                 if (elm->leaf.base.key >= MAXPHYS &&
328                     elm->leaf.base.key - MAXPHYS > hist->key) {
329                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
330                 }
331
332                 /*
333                  * Data-range of record does not cover the key.
334                  */
335                 if (elm->leaf.base.key - elm->leaf.data_len > hist->key)
336                         return;
337
338         } else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
339                 /*
340                  * Adjust nxt_key
341                  */
342                 if (hist->nxt_key > elm->leaf.base.key &&
343                     hist->key < elm->leaf.base.key) {
344                         hist->nxt_key = elm->leaf.base.key;
345                 }
346
347                 /*
348                  * Record is beyond the requested key.
349                  */
350                 if (elm->leaf.base.key > hist->key)
351                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
352         }
353
354         /*
355          * Add create_tid if it is in-bounds.
356          */
357         i = hist->count;
358         if ((i == 0 ||
359              elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) &&
360             elm->leaf.base.create_tid >= hist->beg_tid &&
361             elm->leaf.base.create_tid < hist->end_tid) {
362                 if (hist->count == HAMMER_MAX_HISTORY_ELMS) {
363                         hist->nxt_tid = elm->leaf.base.create_tid;
364                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
365                         return;
366                 }
367                 hist->hist_ary[i].tid = elm->leaf.base.create_tid;
368                 hist->hist_ary[i].time32 = elm->leaf.create_ts;
369                 ++hist->count;
370         }
371
372         /*
373          * Add delete_tid if it is in-bounds.  Note that different portions
374          * of the history may have overlapping data ranges with different
375          * delete_tid's.  If this case occurs the delete_tid may match the
376          * create_tid of a following record.  XXX
377          *
378          *      [        ]
379          *            [     ]
380          */
381         i = hist->count;
382         if (elm->leaf.base.delete_tid &&
383             elm->leaf.base.delete_tid >= hist->beg_tid &&
384             elm->leaf.base.delete_tid < hist->end_tid) {
385                 if (i == HAMMER_MAX_HISTORY_ELMS) {
386                         hist->nxt_tid = elm->leaf.base.delete_tid;
387                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
388                         return;
389                 }
390                 hist->hist_ary[i].tid = elm->leaf.base.delete_tid;
391                 hist->hist_ary[i].time32 = elm->leaf.delete_ts;
392                 ++hist->count;
393         }
394 }
395
396 /*
397  * Acquire synchronization TID
398  */
399 static
400 int
401 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip,
402                    struct hammer_ioc_synctid *std)
403 {
404         hammer_mount_t hmp = ip->hmp;
405         int error = 0;
406
407         switch(std->op) {
408         case HAMMER_SYNCTID_NONE:
409                 std->tid = hmp->flusher.tid;    /* inaccurate */
410                 break;
411         case HAMMER_SYNCTID_ASYNC:
412                 hammer_queue_inodes_flusher(hmp, MNT_NOWAIT);
413                 hammer_flusher_async(hmp, NULL);
414                 std->tid = hmp->flusher.tid;    /* inaccurate */
415                 break;
416         case HAMMER_SYNCTID_SYNC1:
417                 hammer_queue_inodes_flusher(hmp, MNT_WAIT);
418                 hammer_flusher_sync(hmp);
419                 std->tid = hmp->flusher.tid;
420                 break;
421         case HAMMER_SYNCTID_SYNC2:
422                 hammer_queue_inodes_flusher(hmp, MNT_WAIT);
423                 hammer_flusher_sync(hmp);
424                 std->tid = hmp->flusher.tid;
425                 hammer_flusher_sync(hmp);
426                 break;
427         default:
428                 error = EOPNOTSUPP;
429                 break;
430         }
431         return(error);
432 }
433
434 /*
435  * Retrieve version info.
436  *
437  * Load min_version, wip_version, and max_versino.  If cur_version is passed
438  * as 0 then load the current version into cur_version.  Load the description
439  * for cur_version into the description array.
440  *
441  * Returns 0 on success, EINVAL if cur_version is non-zero and set to an
442  * unsupported value.
443  */
444 static
445 int
446 hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip,
447                    struct hammer_ioc_version *ver)
448 {
449         int error = 0;
450
451         ver->min_version = HAMMER_VOL_VERSION_MIN;
452         ver->wip_version = HAMMER_VOL_VERSION_WIP;
453         ver->max_version = HAMMER_VOL_VERSION_MAX;
454         if (ver->cur_version == 0)
455                 ver->cur_version = trans->hmp->version;
456         switch(ver->cur_version) {
457         case 1:
458                 ksnprintf(ver->description, sizeof(ver->description),
459                          "2.0 - First HAMMER release");
460                 break;
461         case 2:
462                 ksnprintf(ver->description, sizeof(ver->description),
463                          "2.3 - New directory entry layout");
464                 break;
465         default:
466                 ksnprintf(ver->description, sizeof(ver->description),
467                          "Unknown");
468                 error = EINVAL;
469                 break;
470         }
471         return(error);
472 };
473
474 /*
475  * Set version info
476  */
477 static
478 int
479 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip,
480                    struct hammer_ioc_version *ver)
481 {
482         struct hammer_cursor cursor;
483         hammer_volume_t volume;
484         int error;
485
486         if (ver->cur_version < trans->hmp->version)
487                 return(EINVAL);
488         if (ver->cur_version == trans->hmp->version)
489                 return(0);
490         if (ver->cur_version > HAMMER_VOL_VERSION_MAX)
491                 return(EINVAL);
492         if (trans->hmp->ronly)
493                 return(EROFS);
494
495         /*
496          * Update the root volume header and the version cached in
497          * the hammer_mount structure.
498          */
499         error = hammer_init_cursor(trans, &cursor, NULL, NULL);
500         if (error)
501                 goto failed;
502         hammer_sync_lock_sh(trans);
503
504         volume = hammer_get_root_volume(cursor.trans->hmp, &error);
505         KKASSERT(error == 0);
506         hammer_modify_volume_field(cursor.trans, volume, vol_version);
507         volume->ondisk->vol_version = ver->cur_version;
508         cursor.trans->hmp->version = ver->cur_version;
509         hammer_modify_volume_done(volume);
510         hammer_rel_volume(volume, 0);
511
512         hammer_sync_unlock(trans);
513 failed:
514         ver->head.error = error;
515         hammer_done_cursor(&cursor);
516         return(0);
517 }
518
519 /*
520  * Get information
521  */
522 static
523 int
524 hammer_ioc_get_info(hammer_transaction_t trans, struct hammer_ioc_info *info) {
525
526         struct hammer_volume_ondisk     *od = trans->hmp->rootvol->ondisk;
527         struct hammer_mount             *hm = trans->hmp;
528
529         /* Fill the structure with the necessary information */
530         _hammer_checkspace(hm, HAMMER_CHKSPC_WRITE, &info->rsvbigblocks);
531         info->rsvbigblocks = info->rsvbigblocks >> HAMMER_LARGEBLOCK_BITS;
532         strlcpy(info->vol_name, od->vol_name, sizeof(od->vol_name));
533
534         info->vol_fsid = hm->fsid;
535         info->vol_fstype = od->vol_fstype;
536         info->version = hm->version;
537
538         info->inodes = od->vol0_stat_inodes;
539         info->bigblocks = od->vol0_stat_bigblocks;
540         info->freebigblocks = od->vol0_stat_freebigblocks;
541         info->nvolumes = hm->nvolumes;
542
543         return 0;
544 }
545