5ed70bed997b0691b42e9590e81e4ce477ec024a
[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         default:
505                 ksnprintf(ver->description, sizeof(ver->description),
506                          "Unknown");
507                 error = EINVAL;
508                 break;
509         }
510         return(error);
511 };
512
513 /*
514  * Set version info
515  */
516 static
517 int
518 hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip,
519                    struct hammer_ioc_version *ver)
520 {
521         struct hammer_cursor cursor;
522         hammer_volume_t volume;
523         int error;
524
525         if (ver->cur_version < trans->hmp->version)
526                 return(EINVAL);
527         if (ver->cur_version == trans->hmp->version)
528                 return(0);
529         if (ver->cur_version > HAMMER_VOL_VERSION_MAX)
530                 return(EINVAL);
531         if (trans->hmp->ronly)
532                 return(EROFS);
533
534         /*
535          * Update the root volume header and the version cached in
536          * the hammer_mount structure.
537          */
538         error = hammer_init_cursor(trans, &cursor, NULL, NULL);
539         if (error)
540                 goto failed;
541         hammer_sync_lock_sh(trans);
542
543         volume = hammer_get_root_volume(cursor.trans->hmp, &error);
544         KKASSERT(error == 0);
545         hammer_modify_volume_field(cursor.trans, volume, vol_version);
546         volume->ondisk->vol_version = ver->cur_version;
547         cursor.trans->hmp->version = ver->cur_version;
548         hammer_modify_volume_done(volume);
549         hammer_rel_volume(volume, 0);
550
551         hammer_sync_unlock(trans);
552 failed:
553         ver->head.error = error;
554         hammer_done_cursor(&cursor);
555         return(0);
556 }
557
558 /*
559  * Get information
560  */
561 static
562 int
563 hammer_ioc_get_info(hammer_transaction_t trans, struct hammer_ioc_info *info) {
564
565         struct hammer_volume_ondisk     *od = trans->hmp->rootvol->ondisk;
566         struct hammer_mount             *hm = trans->hmp;
567
568         /* Fill the structure with the necessary information */
569         _hammer_checkspace(hm, HAMMER_CHKSPC_WRITE, &info->rsvbigblocks);
570         info->rsvbigblocks = info->rsvbigblocks >> HAMMER_LARGEBLOCK_BITS;
571         strlcpy(info->vol_name, od->vol_name, sizeof(od->vol_name));
572
573         info->vol_fsid = hm->fsid;
574         info->vol_fstype = od->vol_fstype;
575         info->version = hm->version;
576
577         info->inodes = od->vol0_stat_inodes;
578         info->bigblocks = od->vol0_stat_bigblocks;
579         info->freebigblocks = od->vol0_stat_freebigblocks;
580         info->nvolumes = hm->nvolumes;
581
582         return 0;
583 }
584
585 /*
586  * Add a snapshot transction id(s) to the list of snapshots.
587  *
588  * NOTE: Records are created with an allocated TID.  If a flush cycle
589  *       is in progress the record may be synced in the current flush
590  *       cycle and the volume header will reflect the allocation of the
591  *       TID, but the synchronization point may not catch up to the
592  *       TID until the next flush cycle.
593  */
594 static
595 int
596 hammer_ioc_add_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
597                         struct hammer_ioc_snapshot *snap)
598 {
599         hammer_mount_t hmp = ip->hmp;
600         struct hammer_btree_leaf_elm leaf;
601         struct hammer_cursor cursor;
602         int error;
603
604         /*
605          * Validate structure
606          */
607         if (snap->count > HAMMER_SNAPS_PER_IOCTL)
608                 return (EINVAL);
609         if (snap->index > snap->count)
610                 return (EINVAL);
611
612         hammer_lock_ex(&hmp->snapshot_lock);
613 again:
614         /*
615          * Look for keys starting after the previous iteration, or at
616          * the beginning if snap->count is 0.
617          */
618         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
619         if (error) {
620                 hammer_done_cursor(&cursor);
621                 return(error);
622         }
623
624         cursor.asof = HAMMER_MAX_TID;
625         cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF;
626
627         bzero(&leaf, sizeof(leaf));
628         leaf.base.obj_id = HAMMER_OBJID_ROOT;
629         leaf.base.rec_type = HAMMER_RECTYPE_SNAPSHOT;
630         leaf.base.create_tid = hammer_alloc_tid(hmp, 1);
631         leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
632         leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
633         leaf.data_len = sizeof(struct hammer_snapshot_data);
634
635         while (snap->index < snap->count) {
636                 leaf.base.key = (int64_t)snap->snaps[snap->index].tid;
637                 cursor.key_beg = leaf.base;
638                 error = hammer_btree_lookup(&cursor);
639                 if (error == 0) {
640                         error = EEXIST;
641                         break;
642                 }
643
644                 cursor.flags &= ~HAMMER_CURSOR_ASOF;
645                 error = hammer_create_at_cursor(&cursor, &leaf,
646                                                 &snap->snaps[snap->index],
647                                                 HAMMER_CREATE_MODE_SYS);
648                 if (error == EDEADLK) {
649                         hammer_done_cursor(&cursor);
650                         goto again;
651                 }
652                 cursor.flags |= HAMMER_CURSOR_ASOF;
653                 if (error)
654                         break;
655                 ++snap->index;
656         }
657         snap->head.error = error;
658         hammer_done_cursor(&cursor);
659         hammer_unlock(&hmp->snapshot_lock);
660         return(0);
661 }
662
663 /*
664  * Delete snapshot transaction id(s) from the list of snapshots.
665  */
666 static
667 int
668 hammer_ioc_del_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
669                         struct hammer_ioc_snapshot *snap)
670 {
671         hammer_mount_t hmp = ip->hmp;
672         struct hammer_cursor cursor;
673         int error;
674
675         /*
676          * Validate structure
677          */
678         if (snap->count > HAMMER_SNAPS_PER_IOCTL)
679                 return (EINVAL);
680         if (snap->index > snap->count)
681                 return (EINVAL);
682
683         hammer_lock_ex(&hmp->snapshot_lock);
684 again:
685         /*
686          * Look for keys starting after the previous iteration, or at
687          * the beginning if snap->count is 0.
688          */
689         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
690         if (error) {
691                 hammer_done_cursor(&cursor);
692                 return(error);
693         }
694
695         cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
696         cursor.key_beg.create_tid = 0;
697         cursor.key_beg.delete_tid = 0;
698         cursor.key_beg.obj_type = 0;
699         cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT;
700         cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
701         cursor.asof = HAMMER_MAX_TID;
702         cursor.flags |= HAMMER_CURSOR_ASOF;
703
704         while (snap->index < snap->count) {
705                 cursor.key_beg.key = (int64_t)snap->snaps[snap->index].tid;
706                 error = hammer_btree_lookup(&cursor);
707                 if (error)
708                         break;
709                 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF);
710                 if (error)
711                         break;
712                 error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY,
713                                                 0, 0, 0, NULL);
714                 if (error == EDEADLK) {
715                         hammer_done_cursor(&cursor);
716                         goto again;
717                 }
718                 if (error)
719                         break;
720                 ++snap->index;
721         }
722         snap->head.error = error;
723         hammer_done_cursor(&cursor);
724         hammer_unlock(&hmp->snapshot_lock);
725         return(0);
726 }
727
728 /*
729  * Retrieve as many snapshot ids as possible or until the array is
730  * full, starting after the last transction id passed in.  If count
731  * is 0 we retrieve starting at the beginning.
732  *
733  * NOTE: Because the b-tree key field is signed but transaction ids
734  *       are unsigned the returned list will be signed-sorted instead
735  *       of unsigned sorted.  The Caller must still sort the aggregate
736  *       results.
737  */
738 static
739 int
740 hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
741                         struct hammer_ioc_snapshot *snap)
742 {
743         struct hammer_cursor cursor;
744         int error;
745
746         /*
747          * Validate structure
748          */
749         if (snap->index != 0)
750                 return (EINVAL);
751         if (snap->count > HAMMER_SNAPS_PER_IOCTL)
752                 return (EINVAL);
753
754         /*
755          * Look for keys starting after the previous iteration, or at
756          * the beginning if snap->count is 0.
757          */
758         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
759         if (error) {
760                 hammer_done_cursor(&cursor);
761                 return(error);
762         }
763
764         cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
765         cursor.key_beg.create_tid = 0;
766         cursor.key_beg.delete_tid = 0;
767         cursor.key_beg.obj_type = 0;
768         cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT;
769         cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
770         if (snap->count == 0)
771                 cursor.key_beg.key = HAMMER_MIN_KEY;
772         else
773                 cursor.key_beg.key = (int64_t)snap->snaps[snap->count - 1].tid + 1;
774
775         cursor.key_end = cursor.key_beg;
776         cursor.key_end.key = HAMMER_MAX_KEY;
777         cursor.asof = HAMMER_MAX_TID;
778         cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE | HAMMER_CURSOR_ASOF;
779
780         snap->count = 0;
781
782         error = hammer_btree_first(&cursor);
783         while (error == 0 && snap->count < HAMMER_SNAPS_PER_IOCTL) {
784                 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF);
785                 if (error)
786                         break;
787                 if (cursor.leaf->base.rec_type == HAMMER_RECTYPE_SNAPSHOT) {
788                         error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF |
789                                                               HAMMER_CURSOR_GET_DATA);
790                         snap->snaps[snap->count] = cursor.data->snap;
791                         ++snap->count;
792                 }
793                 error = hammer_btree_iterate(&cursor);
794         }
795
796         if (error == ENOENT) {
797                 snap->head.flags |= HAMMER_IOC_SNAPSHOT_EOF;
798                 error = 0;
799         }
800         snap->head.error = error;
801         hammer_done_cursor(&cursor);
802         return(0);
803 }
804
805 /*
806  * Retrieve the PFS hammer cleanup utility config record.  This is
807  * different (newer than) the PFS config.
808  */
809 static
810 int
811 hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip,
812                         struct hammer_ioc_config *config)
813 {
814         struct hammer_cursor cursor;
815         int error;
816
817         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
818         if (error) {
819                 hammer_done_cursor(&cursor);
820                 return(error);
821         }
822
823         cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
824         cursor.key_beg.create_tid = 0;
825         cursor.key_beg.delete_tid = 0;
826         cursor.key_beg.obj_type = 0;
827         cursor.key_beg.rec_type = HAMMER_RECTYPE_CONFIG;
828         cursor.key_beg.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
829         cursor.key_beg.key = 0;         /* config space page 0 */
830
831         cursor.asof = HAMMER_MAX_TID;
832         cursor.flags |= HAMMER_CURSOR_ASOF;
833
834         error = hammer_btree_lookup(&cursor);
835         if (error == 0) {
836                 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF |
837                                                       HAMMER_CURSOR_GET_DATA);
838                 if (error == 0)
839                         config->config = cursor.data->config;
840         }
841         /* error can be ENOENT */
842         config->head.error = error;
843         hammer_done_cursor(&cursor);
844         return(0);
845 }
846
847 /*
848  * Retrieve the PFS hammer cleanup utility config record.  This is
849  * different (newer than) the PFS config.
850  *
851  * This is kinda a hack.
852  */
853 static
854 int
855 hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip,
856                         struct hammer_ioc_config *config)
857 {
858         struct hammer_btree_leaf_elm leaf;
859         struct hammer_cursor cursor;
860         hammer_mount_t hmp = ip->hmp;
861         int error;
862
863 again:
864         error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
865         if (error) {
866                 hammer_done_cursor(&cursor);
867                 return(error);
868         }
869
870         bzero(&leaf, sizeof(leaf));
871         leaf.base.obj_id = HAMMER_OBJID_ROOT;
872         leaf.base.rec_type = HAMMER_RECTYPE_CONFIG;
873         leaf.base.create_tid = hammer_alloc_tid(hmp, 1);
874         leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
875         leaf.base.localization = ip->obj_localization + HAMMER_LOCALIZE_INODE;
876         leaf.base.key = 0;      /* page 0 */
877         leaf.data_len = sizeof(struct hammer_config_data);
878
879         cursor.key_beg = leaf.base;
880
881         cursor.asof = HAMMER_MAX_TID;
882         cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF;
883
884         error = hammer_btree_lookup(&cursor);
885         if (error == 0) {
886                 error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_LEAF |
887                                                       HAMMER_CURSOR_GET_DATA);
888                 error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY,
889                                                 0, 0, 0, NULL);
890                 if (error == EDEADLK) {
891                         hammer_done_cursor(&cursor);
892                         goto again;
893                 }
894         }
895         if (error == ENOENT)
896                 error = 0;
897         if (error == 0) {
898                 cursor.flags &= ~HAMMER_CURSOR_ASOF;
899                 cursor.key_beg = leaf.base;
900                 error = hammer_create_at_cursor(&cursor, &leaf,
901                                                 &config->config,
902                                                 HAMMER_CREATE_MODE_SYS);
903                 if (error == EDEADLK) {
904                         hammer_done_cursor(&cursor);
905                         goto again;
906                 }
907         }
908         config->head.error = error;
909         hammer_done_cursor(&cursor);
910         return(0);
911 }