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