Merge branch 'vendor/BZIP'
[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 static int hammer_ioc_add_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
52                                 struct hammer_ioc_snapshot *snap);
53 static int hammer_ioc_del_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
54                                 struct hammer_ioc_snapshot *snap);
55 static int hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
56                                 struct hammer_ioc_snapshot *snap);
57 static int hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip,
58                                 struct hammer_ioc_config *snap);
59 static int hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip,
60                                 struct hammer_ioc_config *snap);
61
62 int
63 hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
64              struct ucred *cred)
65 {
66         struct hammer_transaction trans;
67         int error;
68
69         error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0);
70
71         hammer_start_transaction(&trans, ip->hmp);
72
73         switch(com) {
74         case HAMMERIOC_PRUNE:
75                 if (error == 0) {
76                         error = hammer_ioc_prune(&trans, ip,
77                                         (struct hammer_ioc_prune *)data);
78                 }
79                 break;
80         case HAMMERIOC_GETHISTORY:
81                 error = hammer_ioc_gethistory(&trans, ip,
82                                         (struct hammer_ioc_history *)data);
83                 break;
84         case HAMMERIOC_REBLOCK:
85                 if (error == 0) {
86                         error = hammer_ioc_reblock(&trans, ip,
87                                         (struct hammer_ioc_reblock *)data);
88                 }
89                 break;
90         case HAMMERIOC_REBALANCE:
91                 /*
92                  * Rebalancing needs to lock a lot of B-Tree nodes.  The
93                  * children and children's children.  Systems with very
94                  * little memory will not be able to do it.
95                  */
96                 if (error == 0 && nbuf < HAMMER_REBALANCE_MIN_BUFS) {
97                         kprintf("hammer: System has insufficient buffers "
98                                 "to rebalance the tree.  nbuf < %d\n",
99                                 HAMMER_REBALANCE_MIN_BUFS);
100                         error = ENOSPC;
101                 }
102                 if (error == 0) {
103                         error = hammer_ioc_rebalance(&trans, ip,
104                                         (struct hammer_ioc_rebalance *)data);
105                 }
106                 break;
107         case HAMMERIOC_SYNCTID:
108                 error = hammer_ioc_synctid(&trans, ip,
109                                         (struct hammer_ioc_synctid *)data);
110                 break;
111         case HAMMERIOC_GET_PSEUDOFS:
112                 error = hammer_ioc_get_pseudofs(&trans, ip,
113                                     (struct hammer_ioc_pseudofs_rw *)data);
114                 break;
115         case HAMMERIOC_SET_PSEUDOFS:
116                 if (error == 0) {
117                         error = hammer_ioc_set_pseudofs(&trans, ip, cred,
118                                     (struct hammer_ioc_pseudofs_rw *)data);
119                 }
120                 break;
121         case HAMMERIOC_UPG_PSEUDOFS:
122                 if (error == 0) {
123                         error = hammer_ioc_upgrade_pseudofs(&trans, ip, 
124                                     (struct hammer_ioc_pseudofs_rw *)data);
125                 }
126                 break;
127         case HAMMERIOC_DGD_PSEUDOFS:
128                 if (error == 0) {
129                         error = hammer_ioc_downgrade_pseudofs(&trans, ip,
130                                     (struct hammer_ioc_pseudofs_rw *)data);
131                 }
132                 break;
133         case HAMMERIOC_RMR_PSEUDOFS:
134                 if (error == 0) {
135                         error = hammer_ioc_destroy_pseudofs(&trans, ip,
136                                     (struct hammer_ioc_pseudofs_rw *)data);
137                 }
138                 break;
139         case HAMMERIOC_WAI_PSEUDOFS:
140                 if (error == 0) {
141                         error = hammer_ioc_wait_pseudofs(&trans, ip,
142                                     (struct hammer_ioc_pseudofs_rw *)data);
143                 }
144                 break;
145         case HAMMERIOC_MIRROR_READ:
146                 if (error == 0) {
147                         error = hammer_ioc_mirror_read(&trans, ip,
148                                     (struct hammer_ioc_mirror_rw *)data);
149                 }
150                 break;
151         case HAMMERIOC_MIRROR_WRITE:
152                 if (error == 0) {
153                         error = hammer_ioc_mirror_write(&trans, ip,
154                                     (struct hammer_ioc_mirror_rw *)data);
155                 }
156                 break;
157         case HAMMERIOC_GET_VERSION:
158                 error = hammer_ioc_get_version(&trans, ip, 
159                                     (struct hammer_ioc_version *)data);
160                 break;
161         case HAMMERIOC_GET_INFO:
162                 error = hammer_ioc_get_info(&trans,
163                                     (struct hammer_ioc_info *)data);
164                 break;
165         case HAMMERIOC_SET_VERSION:
166                 if (error == 0) {
167                         error = hammer_ioc_set_version(&trans, ip, 
168                                             (struct hammer_ioc_version *)data);
169                 }
170                 break;
171         case HAMMERIOC_ADD_VOLUME:
172                 if (error == 0) {
173                         error = priv_check_cred(cred, PRIV_HAMMER_VOLUME, 0);
174                         if (error == 0)
175                                 error = hammer_ioc_volume_add(&trans, ip,
176                                             (struct hammer_ioc_volume *)data);
177                 }
178                 break;
179         case HAMMERIOC_DEL_VOLUME:
180                 if (error == 0) {
181                         error = priv_check_cred(cred, PRIV_HAMMER_VOLUME, 0);
182                         if (error == 0)
183                                 error = hammer_ioc_volume_del(&trans, ip,
184                                             (struct hammer_ioc_volume *)data);
185                 }
186                 break;
187         case HAMMERIOC_ADD_SNAPSHOT:
188                 if (error == 0) {
189                         error = hammer_ioc_add_snapshot(
190                                         &trans, ip, (struct hammer_ioc_snapshot *)data);
191                 }
192                 break;
193         case HAMMERIOC_DEL_SNAPSHOT:
194                 if (error == 0) {
195                         error = hammer_ioc_del_snapshot(
196                                         &trans, ip, (struct hammer_ioc_snapshot *)data);
197                 }
198                 break;
199         case HAMMERIOC_GET_SNAPSHOT:
200                 error = hammer_ioc_get_snapshot(
201                                         &trans, ip, (struct hammer_ioc_snapshot *)data);
202                 break;
203         case HAMMERIOC_GET_CONFIG:
204                 error = hammer_ioc_get_config(
205                                         &trans, ip, (struct hammer_ioc_config *)data);
206                 break;
207         case HAMMERIOC_SET_CONFIG:
208                 if (error == 0) {
209                         error = hammer_ioc_set_config(
210                                         &trans, ip, (struct hammer_ioc_config *)data);
211                 }
212                 break;
213         default:
214                 error = EOPNOTSUPP;
215                 break;
216         }
217         hammer_done_transaction(&trans);
218         return (error);
219 }
220
221 /*
222  * Iterate through an object's inode or an object's records and record
223  * modification TIDs.
224  */
225 static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
226                         hammer_btree_elm_t elm);
227
228 static
229 int
230 hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip,
231                       struct hammer_ioc_history *hist)
232 {
233         struct hammer_cursor cursor;
234         hammer_btree_elm_t elm;
235         int error;
236
237         /*
238          * Validate the structure and initialize for return.
239          */
240         if (hist->beg_tid > hist->end_tid)
241                 return(EINVAL);
242         if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
243                 if (hist->key > hist->nxt_key)
244                         return(EINVAL);
245         }
246
247         hist->obj_id = ip->obj_id;
248         hist->count = 0;
249         hist->nxt_tid = hist->end_tid;
250         hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID;
251         hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY;
252         hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF;
253         hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED;
254         if ((ip->flags & HAMMER_INODE_MODMASK) & 
255             ~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) {
256                 hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED;
257         }
258
259         /*
260          * Setup the cursor.  We can't handle undeletable records
261          * (create_tid of 0) at the moment.  A create_tid of 0 has
262          * a special meaning and cannot be specified in the cursor.
263          */
264         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
265         if (error) {
266                 hammer_done_cursor(&cursor);
267                 return(error);
268         }
269
270         cursor.key_beg.obj_id = hist->obj_id;
271         cursor.key_beg.create_tid = hist->beg_tid;
272         cursor.key_beg.delete_tid = 0;
273         cursor.key_beg.obj_type = 0;
274         if (cursor.key_beg.create_tid == HAMMER_MIN_TID)
275                 cursor.key_beg.create_tid = 1;
276
277         cursor.key_end.obj_id = hist->obj_id;
278         cursor.key_end.create_tid = hist->end_tid;
279         cursor.key_end.delete_tid = 0;
280         cursor.key_end.obj_type = 0;
281
282         cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE;
283
284         if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
285                 /*
286                  * key-range within the file.  For a regular file the
287                  * on-disk key represents BASE+LEN, not BASE, so the
288                  * first possible record containing the offset 'key'
289                  * has an on-disk key of (key + 1).
290                  */
291                 cursor.key_beg.key = hist->key;
292                 cursor.key_end.key = HAMMER_MAX_KEY;
293                 cursor.key_beg.localization = ip->obj_localization + 
294                                               HAMMER_LOCALIZE_MISC;
295                 cursor.key_end.localization = ip->obj_localization + 
296                                               HAMMER_LOCALIZE_MISC;
297
298                 switch(ip->ino_data.obj_type) {
299                 case HAMMER_OBJTYPE_REGFILE:
300                         ++cursor.key_beg.key;
301                         cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
302                         break;
303                 case HAMMER_OBJTYPE_DIRECTORY:
304                         cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
305                         cursor.key_beg.localization = ip->obj_localization +
306                                                 hammer_dir_localization(ip);
307                         cursor.key_end.localization = ip->obj_localization +
308                                                 hammer_dir_localization(ip);
309                         break;
310                 case HAMMER_OBJTYPE_DBFILE:
311                         cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
312                         break;
313                 default:
314                         error = EINVAL;
315                         break;
316                 }
317                 cursor.key_end.rec_type = cursor.key_beg.rec_type;
318         } else {
319                 /*
320                  * The inode itself.
321                  */
322                 cursor.key_beg.key = 0;
323                 cursor.key_end.key = 0;
324                 cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
325                 cursor.key_end.rec_type = HAMMER_RECTYPE_INODE;
326                 cursor.key_beg.localization = ip->obj_localization +
327                                               HAMMER_LOCALIZE_INODE;
328                 cursor.key_end.localization = ip->obj_localization +
329                                               HAMMER_LOCALIZE_INODE;
330         }
331
332         error = hammer_btree_first(&cursor);
333         while (error == 0) {
334                 elm = &cursor.node->ondisk->elms[cursor.index];
335
336                 add_history(ip, hist, elm);
337                 if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID |
338                                         HAMMER_IOC_HISTORY_NEXT_KEY |
339                                         HAMMER_IOC_HISTORY_EOF)) {
340                         break;
341                 }
342                 error = hammer_btree_iterate(&cursor);
343         }
344         if (error == ENOENT) {
345                 hist->head.flags |= HAMMER_IOC_HISTORY_EOF;
346                 error = 0;
347         }
348         hammer_done_cursor(&cursor);
349         return(error);
350 }
351
352 /*
353  * Add the scanned element to the ioctl return structure.  Some special
354  * casing is required for regular files to accomodate how data ranges are
355  * stored on-disk.
356  */
357 static void
358 add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
359             hammer_btree_elm_t elm)
360 {
361         int i;
362
363         if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD)
364                 return;
365         if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) &&
366             ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) {
367                 /*
368                  * Adjust nxt_key
369                  */
370                 if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len &&
371                     hist->key < elm->leaf.base.key - elm->leaf.data_len) {
372                         hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len;
373                 }
374                 if (hist->nxt_key > elm->leaf.base.key)
375                         hist->nxt_key = elm->leaf.base.key;
376
377                 /*
378                  * Record is beyond MAXPHYS, there won't be any more records
379                  * in the iteration covering the requested offset (key).
380                  */
381                 if (elm->leaf.base.key >= MAXPHYS &&
382                     elm->leaf.base.key - MAXPHYS > hist->key) {
383                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
384                 }
385
386                 /*
387                  * Data-range of record does not cover the key.
388                  */
389                 if (elm->leaf.base.key - elm->leaf.data_len > hist->key)
390                         return;
391
392         } else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
393                 /*
394                  * Adjust nxt_key
395                  */
396                 if (hist->nxt_key > elm->leaf.base.key &&
397                     hist->key < elm->leaf.base.key) {
398                         hist->nxt_key = elm->leaf.base.key;
399                 }
400
401                 /*
402                  * Record is beyond the requested key.
403                  */
404                 if (elm->leaf.base.key > hist->key)
405                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
406         }
407
408         /*
409          * Add create_tid if it is in-bounds.
410          */
411         i = hist->count;
412         if ((i == 0 ||
413              elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) &&
414             elm->leaf.base.create_tid >= hist->beg_tid &&
415             elm->leaf.base.create_tid < hist->end_tid) {
416                 if (hist->count == HAMMER_MAX_HISTORY_ELMS) {
417                         hist->nxt_tid = elm->leaf.base.create_tid;
418                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
419                         return;
420                 }
421                 hist->hist_ary[i].tid = elm->leaf.base.create_tid;
422                 hist->hist_ary[i].time32 = elm->leaf.create_ts;
423                 ++hist->count;
424         }
425
426         /*
427          * Add delete_tid if it is in-bounds.  Note that different portions
428          * of the history may have overlapping data ranges with different
429          * delete_tid's.  If this case occurs the delete_tid may match the
430          * create_tid of a following record.  XXX
431          *
432          *      [        ]
433          *            [     ]
434          */
435         i = hist->count;
436         if (elm->leaf.base.delete_tid &&
437             elm->leaf.base.delete_tid >= hist->beg_tid &&
438             elm->leaf.base.delete_tid < hist->end_tid) {
439                 if (i == HAMMER_MAX_HISTORY_ELMS) {
440                         hist->nxt_tid = elm->leaf.base.delete_tid;
441                         hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
442                         return;
443                 }
444                 hist->hist_ary[i].tid = elm->leaf.base.delete_tid;
445                 hist->hist_ary[i].time32 = elm->leaf.delete_ts;
446                 ++hist->count;
447         }
448 }
449
450 /*
451  * Acquire synchronization TID
452  */
453 static
454 int
455 hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip,
456                    struct hammer_ioc_synctid *std)
457 {
458         hammer_mount_t hmp = ip->hmp;
459         int error = 0;
460
461         switch(std->op) {
462         case HAMMER_SYNCTID_NONE:
463                 std->tid = hmp->flusher.tid;    /* inaccurate */
464                 break;
465         case HAMMER_SYNCTID_ASYNC:
466                 hammer_queue_inodes_flusher(hmp, MNT_NOWAIT);
467                 hammer_flusher_async(hmp, NULL);
468                 std->tid = hmp->flusher.tid;    /* inaccurate */
469                 break;
470         case HAMMER_SYNCTID_SYNC1:
471                 hammer_queue_inodes_flusher(hmp, MNT_WAIT);
472                 hammer_flusher_sync(hmp);
473                 std->tid = hmp->flusher.tid;
474                 break;
475         case HAMMER_SYNCTID_SYNC2:
476                 hammer_queue_inodes_flusher(hmp, MNT_WAIT);
477                 hammer_flusher_sync(hmp);
478                 std->tid = hmp->flusher.tid;
479                 hammer_flusher_sync(hmp);
480                 break;
481         default:
482                 error = EOPNOTSUPP;
483                 break;
484         }
485         return(error);
486 }
487
488 /*
489  * Retrieve version info.
490  *
491  * Load min_version, wip_version, and max_versino.  If cur_version is passed
492  * as 0 then load the current version into cur_version.  Load the description
493  * for cur_version into the description array.
494  *
495  * Returns 0 on success, EINVAL if cur_version is non-zero and set to an
496  * unsupported value.
497  */
498 static
499 int
500 hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip,
501                    struct hammer_ioc_version *ver)
502 {
503         int error = 0;
504
505         ver->min_version = HAMMER_VOL_VERSION_MIN;
506         ver->wip_version = HAMMER_VOL_VERSION_WIP;
507         ver->max_version = HAMMER_VOL_VERSION_MAX;
508         if (ver->cur_version == 0)
509                 ver->cur_version = trans->hmp->version;
510         switch(ver->cur_version) {
511         case 1:
512                 ksnprintf(ver->description, sizeof(ver->description),
513                          "First HAMMER release (DragonFly 2.0+)");
514                 break;
515         case 2:
516                 ksnprintf(ver->description, sizeof(ver->description),
517                          "New directory entry layout (DragonFly 2.3+)");
518                 break;
519         case 3:
520                 ksnprintf(ver->description, sizeof(ver->description),
521                          "New snapshot management (DragonFly 2.5+)");
522                 break;
523         case 4:
524                 ksnprintf(ver->description, sizeof(ver->description),
525                          "New undo/flush, faster flush/sync (DragonFly 2.5+)");
526                 break;
527         default:
528                 ksnprintf(ver->description, sizeof(ver->description),
529                          "Unknown");
530                 error = EINVAL;
531                 break;
532         }
533         return(error);
534 };
535
536 /*
537  * Set version info
538  */
539 static
540 int
541 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip,
542                    struct hammer_ioc_version *ver)
543 {
544         hammer_mount_t hmp = trans->hmp;
545         struct hammer_cursor cursor;
546         hammer_volume_t volume;
547         int error;
548         int over = hmp->version;
549
550         /*
551          * Generally do not allow downgrades.  However, version 4 can
552          * be downgraded to version 3.
553          */
554         if (ver->cur_version < hmp->version) {
555                 if (!(ver->cur_version == 3 && hmp->version == 4))
556                         return(EINVAL);
557         }
558         if (ver->cur_version == hmp->version)
559                 return(0);
560         if (ver->cur_version > HAMMER_VOL_VERSION_MAX)
561                 return(EINVAL);
562         if (hmp->ronly)
563                 return(EROFS);
564
565         /*
566          * Update the root volume header and the version cached in
567          * the hammer_mount structure.
568          */
569         error = hammer_init_cursor(trans, &cursor, NULL, NULL);
570         if (error)
571                 goto failed;
572         hammer_lock_ex(&hmp->flusher.finalize_lock);
573         hammer_sync_lock_ex(trans);
574         hmp->version = ver->cur_version;
575
576         /*
577          * If upgrading from version < 4 to version >= 4 the UNDO FIFO
578          * must be reinitialized.
579          */
580         if (over < HAMMER_VOL_VERSION_FOUR &&
581             ver->cur_version >= HAMMER_VOL_VERSION_FOUR) {
582                 kprintf("upgrade undo to version 4\n");
583                 error = hammer_upgrade_undo_4(trans);
584                 if (error)
585                         goto failed;
586         }
587
588         /*
589          * Adjust the version in the volume header
590          */
591         volume = hammer_get_root_volume(hmp, &error);
592         KKASSERT(error == 0);
593         hammer_modify_volume_field(cursor.trans, volume, vol_version);
594         volume->ondisk->vol_version = ver->cur_version;
595         hammer_modify_volume_done(volume);
596         hammer_rel_volume(volume, 0);
597
598         hammer_sync_unlock(trans);
599         hammer_unlock(&hmp->flusher.finalize_lock);
600 failed:
601         ver->head.error = error;
602         hammer_done_cursor(&cursor);
603         return(0);
604 }
605
606 /*
607  * Get information
608  */
609 static
610 int
611 hammer_ioc_get_info(hammer_transaction_t trans, struct hammer_ioc_info *info) {
612
613         struct hammer_volume_ondisk     *od = trans->hmp->rootvol->ondisk;
614         struct hammer_mount             *hm = trans->hmp;
615
616         /* Fill the structure with the necessary information */
617         _hammer_checkspace(hm, HAMMER_CHKSPC_WRITE, &info->rsvbigblocks);
618         info->rsvbigblocks = info->rsvbigblocks >> HAMMER_LARGEBLOCK_BITS;
619         strlcpy(info->vol_name, od->vol_name, sizeof(od->vol_name));
620
621         info->vol_fsid = hm->fsid;
622         info->vol_fstype = od->vol_fstype;
623         info->version = hm->version;
624
625         info->inodes = od->vol0_stat_inodes;
626         info->bigblocks = od->vol0_stat_bigblocks;
627         info->freebigblocks = od->vol0_stat_freebigblocks;
628         info->nvolumes = hm->nvolumes;
629
630         return 0;
631 }
632
633 /*
634  * Add a snapshot transction id(s) to the list of snapshots.
635  *
636  * NOTE: Records are created with an allocated TID.  If a flush cycle
637  *       is in progress the record may be synced in the current flush
638  *       cycle and the volume header will reflect the allocation of the
639  *       TID, but the synchronization point may not catch up to the
640  *       TID until the next flush cycle.
641  */
642 static
643 int
644 hammer_ioc_add_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
645                         struct hammer_ioc_snapshot *snap)
646 {
647         hammer_mount_t hmp = ip->hmp;
648         struct hammer_btree_leaf_elm leaf;
649         struct hammer_cursor cursor;
650         int error;
651
652         /*
653          * Validate structure
654          */
655         if (snap->count > HAMMER_SNAPS_PER_IOCTL)
656                 return (EINVAL);
657         if (snap->index > snap->count)
658                 return (EINVAL);
659
660         hammer_lock_ex(&hmp->snapshot_lock);
661 again:
662         /*
663          * Look for keys starting after the previous iteration, or at
664          * the beginning if snap->count is 0.
665          */
666         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
667         if (error) {
668                 hammer_done_cursor(&cursor);
669                 return(error);
670         }
671
672         cursor.asof = HAMMER_MAX_TID;
673         cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF;
674
675         bzero(&leaf, sizeof(leaf));
676         leaf.base.obj_id = HAMMER_OBJID_ROOT;
677         leaf.base.rec_type = HAMMER_RECTYPE_SNAPSHOT;
678         leaf.base.create_tid = hammer_alloc_tid(hmp, 1);
679         leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
680         leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
681         leaf.data_len = sizeof(struct hammer_snapshot_data);
682
683         while (snap->index < snap->count) {
684                 leaf.base.key = (int64_t)snap->snaps[snap->index].tid;
685                 cursor.key_beg = leaf.base;
686                 error = hammer_btree_lookup(&cursor);
687                 if (error == 0) {
688                         error = EEXIST;
689                         break;
690                 }
691
692                 /*
693                  * NOTE: Must reload key_beg after an ASOF search because
694                  *       the create_tid may have been modified during the
695                  *       search.
696                  */
697                 cursor.flags &= ~HAMMER_CURSOR_ASOF;
698                 cursor.key_beg = leaf.base;
699                 error = hammer_create_at_cursor(&cursor, &leaf,
700                                                 &snap->snaps[snap->index],
701                                                 HAMMER_CREATE_MODE_SYS);
702                 if (error == EDEADLK) {
703                         hammer_done_cursor(&cursor);
704                         goto again;
705                 }
706                 cursor.flags |= HAMMER_CURSOR_ASOF;
707                 if (error)
708                         break;
709                 ++snap->index;
710         }
711         snap->head.error = error;
712         hammer_done_cursor(&cursor);
713         hammer_unlock(&hmp->snapshot_lock);
714         return(0);
715 }
716
717 /*
718  * Delete snapshot transaction id(s) from the list of snapshots.
719  */
720 static
721 int
722 hammer_ioc_del_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
723                         struct hammer_ioc_snapshot *snap)
724 {
725         hammer_mount_t hmp = ip->hmp;
726         struct hammer_cursor cursor;
727         int error;
728
729         /*
730          * Validate structure
731          */
732         if (snap->count > HAMMER_SNAPS_PER_IOCTL)
733                 return (EINVAL);
734         if (snap->index > snap->count)
735                 return (EINVAL);
736
737         hammer_lock_ex(&hmp->snapshot_lock);
738 again:
739         /*
740          * Look for keys starting after the previous iteration, or at
741          * the beginning if snap->count is 0.
742          */
743         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
744         if (error) {
745                 hammer_done_cursor(&cursor);
746                 return(error);
747         }
748
749         cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
750         cursor.key_beg.create_tid = 0;
751         cursor.key_beg.delete_tid = 0;
752         cursor.key_beg.obj_type = 0;
753         cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT;
754         cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
755         cursor.asof = HAMMER_MAX_TID;
756         cursor.flags |= HAMMER_CURSOR_ASOF;
757
758         while (snap->index < snap->count) {
759                 cursor.key_beg.key = (int64_t)snap->snaps[snap->index].tid;
760                 error = hammer_btree_lookup(&cursor);
761                 if (error)
762                         break;
763                 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF);
764                 if (error)
765                         break;
766                 error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY,
767                                                 0, 0, 0, NULL);
768                 if (error == EDEADLK) {
769                         hammer_done_cursor(&cursor);
770                         goto again;
771                 }
772                 if (error)
773                         break;
774                 ++snap->index;
775         }
776         snap->head.error = error;
777         hammer_done_cursor(&cursor);
778         hammer_unlock(&hmp->snapshot_lock);
779         return(0);
780 }
781
782 /*
783  * Retrieve as many snapshot ids as possible or until the array is
784  * full, starting after the last transction id passed in.  If count
785  * is 0 we retrieve starting at the beginning.
786  *
787  * NOTE: Because the b-tree key field is signed but transaction ids
788  *       are unsigned the returned list will be signed-sorted instead
789  *       of unsigned sorted.  The Caller must still sort the aggregate
790  *       results.
791  */
792 static
793 int
794 hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
795                         struct hammer_ioc_snapshot *snap)
796 {
797         struct hammer_cursor cursor;
798         int error;
799
800         /*
801          * Validate structure
802          */
803         if (snap->index != 0)
804                 return (EINVAL);
805         if (snap->count > HAMMER_SNAPS_PER_IOCTL)
806                 return (EINVAL);
807
808         /*
809          * Look for keys starting after the previous iteration, or at
810          * the beginning if snap->count is 0.
811          */
812         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
813         if (error) {
814                 hammer_done_cursor(&cursor);
815                 return(error);
816         }
817
818         cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
819         cursor.key_beg.create_tid = 0;
820         cursor.key_beg.delete_tid = 0;
821         cursor.key_beg.obj_type = 0;
822         cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT;
823         cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
824         if (snap->count == 0)
825                 cursor.key_beg.key = HAMMER_MIN_KEY;
826         else
827                 cursor.key_beg.key = (int64_t)snap->snaps[snap->count - 1].tid + 1;
828
829         cursor.key_end = cursor.key_beg;
830         cursor.key_end.key = HAMMER_MAX_KEY;
831         cursor.asof = HAMMER_MAX_TID;
832         cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE | HAMMER_CURSOR_ASOF;
833
834         snap->count = 0;
835
836         error = hammer_btree_first(&cursor);
837         while (error == 0 && snap->count < HAMMER_SNAPS_PER_IOCTL) {
838                 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF);
839                 if (error)
840                         break;
841                 if (cursor.leaf->base.rec_type == HAMMER_RECTYPE_SNAPSHOT) {
842                         error = hammer_btree_extract(
843                                              &cursor, HAMMER_CURSOR_GET_LEAF |
844                                                       HAMMER_CURSOR_GET_DATA);
845                         snap->snaps[snap->count] = cursor.data->snap;
846
847                         /*
848                          * The snap data tid should match the key but might
849                          * not due to a bug in the HAMMER v3 conversion code.
850                          *
851                          * This error will work itself out over time but we
852                          * have to force a match or the snapshot will not
853                          * be deletable.
854                          */
855                         if (cursor.data->snap.tid !=
856                             (hammer_tid_t)cursor.leaf->base.key) {
857                                 kprintf("HAMMER: lo=%08x snapshot key "
858                                         "0x%016jx data mismatch 0x%016jx\n",
859                                         cursor.key_beg.localization,
860                                         (uintmax_t)cursor.data->snap.tid,
861                                         cursor.leaf->base.key);
862                                 kprintf("HAMMER: Probably left over from the "
863                                         "original v3 conversion, hammer "
864                                         "cleanup should get it eventually\n");
865                                 snap->snaps[snap->count].tid =
866                                         cursor.leaf->base.key;
867                         }
868                         ++snap->count;
869                 }
870                 error = hammer_btree_iterate(&cursor);
871         }
872
873         if (error == ENOENT) {
874                 snap->head.flags |= HAMMER_IOC_SNAPSHOT_EOF;
875                 error = 0;
876         }
877         snap->head.error = error;
878         hammer_done_cursor(&cursor);
879         return(0);
880 }
881
882 /*
883  * Retrieve the PFS hammer cleanup utility config record.  This is
884  * different (newer than) the PFS config.
885  */
886 static
887 int
888 hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip,
889                         struct hammer_ioc_config *config)
890 {
891         struct hammer_cursor cursor;
892         int error;
893
894         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
895         if (error) {
896                 hammer_done_cursor(&cursor);
897                 return(error);
898         }
899
900         cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
901         cursor.key_beg.create_tid = 0;
902         cursor.key_beg.delete_tid = 0;
903         cursor.key_beg.obj_type = 0;
904         cursor.key_beg.rec_type = HAMMER_RECTYPE_CONFIG;
905         cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
906         cursor.key_beg.key = 0;         /* config space page 0 */
907
908         cursor.asof = HAMMER_MAX_TID;
909         cursor.flags |= HAMMER_CURSOR_ASOF;
910
911         error = hammer_btree_lookup(&cursor);
912         if (error == 0) {
913                 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF |
914                                                       HAMMER_CURSOR_GET_DATA);
915                 if (error == 0)
916                         config->config = cursor.data->config;
917         }
918         /* error can be ENOENT */
919         config->head.error = error;
920         hammer_done_cursor(&cursor);
921         return(0);
922 }
923
924 /*
925  * Retrieve the PFS hammer cleanup utility config record.  This is
926  * different (newer than) the PFS config.
927  *
928  * This is kinda a hack.
929  */
930 static
931 int
932 hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip,
933                         struct hammer_ioc_config *config)
934 {
935         struct hammer_btree_leaf_elm leaf;
936         struct hammer_cursor cursor;
937         hammer_mount_t hmp = ip->hmp;
938         int error;
939
940 again:
941         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
942         if (error) {
943                 hammer_done_cursor(&cursor);
944                 return(error);
945         }
946
947         bzero(&leaf, sizeof(leaf));
948         leaf.base.obj_id = HAMMER_OBJID_ROOT;
949         leaf.base.rec_type = HAMMER_RECTYPE_CONFIG;
950         leaf.base.create_tid = hammer_alloc_tid(hmp, 1);
951         leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
952         leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
953         leaf.base.key = 0;      /* page 0 */
954         leaf.data_len = sizeof(struct hammer_config_data);
955
956         cursor.key_beg = leaf.base;
957
958         cursor.asof = HAMMER_MAX_TID;
959         cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF;
960
961         error = hammer_btree_lookup(&cursor);
962         if (error == 0) {
963                 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF |
964                                                       HAMMER_CURSOR_GET_DATA);
965                 error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY,
966                                                 0, 0, 0, NULL);
967                 if (error == EDEADLK) {
968                         hammer_done_cursor(&cursor);
969                         goto again;
970                 }
971         }
972         if (error == ENOENT)
973                 error = 0;
974         if (error == 0) {
975                 /*
976                  * NOTE: Must reload key_beg after an ASOF search because
977                  *       the create_tid may have been modified during the
978                  *       search.
979                  */
980                 cursor.flags &= ~HAMMER_CURSOR_ASOF;
981                 cursor.key_beg = leaf.base;
982                 error = hammer_create_at_cursor(&cursor, &leaf,
983                                                 &config->config,
984                                                 HAMMER_CREATE_MODE_SYS);
985                 if (error == EDEADLK) {
986                         hammer_done_cursor(&cursor);
987                         goto again;
988                 }
989         }
990         config->head.error = error;
991         hammer_done_cursor(&cursor);
992         return(0);
993 }