HAMMER 25/many: Add an ioctl API for HAMMER.
authorMatthew Dillon <dillon@dragonflybsd.org>
Mon, 4 Feb 2008 08:33:17 +0000 (08:33 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Mon, 4 Feb 2008 08:33:17 +0000 (08:33 +0000)
* Add HAMMERIOC_PRUNE - a command which will scan a range of inode numbers
  and prune them according to the supplied list.   This is a preliminary
  implementation.

* Add HAMMERIOC_GETHISTORY - a command which scans the history for a
  particular file or a particular file offset within a file and displays
  it.

sys/conf/files
sys/vfs/hammer/Makefile
sys/vfs/hammer/hammer.h
sys/vfs/hammer/hammer_btree.c
sys/vfs/hammer/hammer_disk.h
sys/vfs/hammer/hammer_ioctl.c [new file with mode: 0644]
sys/vfs/hammer/hammer_ioctl.h [new file with mode: 0644]
sys/vfs/hammer/hammer_object.c
sys/vfs/hammer/hammer_subs.c
sys/vfs/hammer/hammer_vnops.c

index 32df234..8372e83 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/sys/conf/files,v 1.201 2008/02/01 13:18:49 sephe Exp $
+# $DragonFly: src/sys/conf/files,v 1.202 2008/02/04 08:33:14 dillon Exp $
 #
 # The long compile-with and dependency lines are required because of
 # limitations in config: backslash-newline doesn't work in strings, and
@@ -1142,6 +1142,7 @@ vfs/hammer/hammer_transaction.c   optional hammer
 vfs/hammer/hammer_object.c     optional hammer
 vfs/hammer/hammer_spike.c      optional hammer
 vfs/hammer/hammer_recover.c    optional hammer
+vfs/hammer/hammer_ioctl.c      optional hammer
 vm/default_pager.c             standard
 vm/device_pager.c              standard
 vm/phys_pager.c                        standard
index ca03221..e915386 100644 (file)
@@ -1,12 +1,12 @@
 #
-# $DragonFly: src/sys/vfs/hammer/Makefile,v 1.5 2008/01/09 00:46:22 dillon Exp $
+# $DragonFly: src/sys/vfs/hammer/Makefile,v 1.6 2008/02/04 08:33:17 dillon Exp $
 
 KMOD=  hammer
 SRCS=  hammer_vfsops.c hammer_vnops.c hammer_inode.c \
        hammer_subs.c hammer_ondisk.c hammer_io.c \
        hammer_cursor.c hammer_btree.c hammer_transaction.c \
        hammer_alist.c hammer_object.c hammer_spike.c \
-       hammer_recover.c
+       hammer_recover.c hammer_ioctl.c
 
 NOMAN=
 
index b12859e..d500f68 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer.h,v 1.30 2008/01/25 10:36:03 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer.h,v 1.31 2008/02/04 08:33:17 dillon Exp $
  */
 /*
  * This header file contains structures used internally by the HAMMERFS
@@ -56,6 +56,7 @@
 #include "hammer_alist.h"
 #include "hammer_disk.h"
 #include "hammer_mount.h"
+#include "hammer_ioctl.h"
 
 #if defined(_KERNEL) || defined(_KERNEL_STRUCTURES)
 
@@ -517,6 +518,7 @@ int hammer_ip_first(hammer_cursor_t cursor, hammer_inode_t ip);
 int    hammer_ip_next(hammer_cursor_t cursor);
 int    hammer_ip_resolve_data(hammer_cursor_t cursor);
 int    hammer_ip_delete_record(hammer_cursor_t cursor, hammer_tid_t tid);
+int    hammer_delete_at_cursor(hammer_cursor_t cursor, int64_t *stat_bytes);
 int    hammer_ip_check_directory_empty(hammer_transaction_t trans,
                        hammer_inode_t ip);
 int    hammer_sync_hmp(hammer_mount_t hmp, int waitfor);
@@ -692,6 +694,9 @@ int hammer_spike(struct hammer_cursor **spikep);
 int hammer_recover(struct hammer_cluster *cluster);
 int buffer_alist_recover(void *info, int32_t blk, int32_t radix, int32_t count);
 
+int hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
+                       struct ucred *cred);
+
 void hammer_io_init(hammer_io_t io, enum hammer_io_type type);
 int hammer_io_read(struct vnode *devvp, struct hammer_io *io);
 int hammer_io_new(struct vnode *devvp, struct hammer_io *io);
index 7517217..87df345 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_btree.c,v 1.26 2008/01/25 21:50:56 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_btree.c,v 1.27 2008/02/04 08:33:17 dillon Exp $
  */
 
 /*
@@ -2413,7 +2413,7 @@ void
 hammer_print_btree_elm(hammer_btree_elm_t elm, u_int8_t type, int i)
 {
        kprintf("  %2d", i);
-       kprintf("\tobjid        = %016llx\n", elm->base.obj_id);
+       kprintf("\tobj_id       = %016llx\n", elm->base.obj_id);
        kprintf("\tkey          = %016llx\n", elm->base.key);
        kprintf("\tcreate_tid   = %016llx\n", elm->base.create_tid);
        kprintf("\tdelete_tid   = %016llx\n", elm->base.delete_tid);
index 194da52..fcb7fb9 100644 (file)
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_disk.h,v 1.19 2008/01/24 02:14:45 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_disk.h,v 1.20 2008/02/04 08:33:17 dillon Exp $
  */
 
+#ifndef VFS_HAMMER_DISK_H_
+#define VFS_HAMMER_DISK_H_
+
 #ifndef _SYS_UUID_H_
 #include <sys/uuid.h>
 #endif
@@ -702,3 +705,4 @@ union hammer_data_ondisk {
        struct hammer_inode_data inode;
 };
 
+#endif
diff --git a/sys/vfs/hammer/hammer_ioctl.c b/sys/vfs/hammer/hammer_ioctl.c
new file mode 100644 (file)
index 0000000..eff58c8
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.c,v 1.1 2008/02/04 08:33:17 dillon Exp $
+ */
+
+#include "hammer.h"
+
+static int hammer_ioc_prune(hammer_inode_t ip,
+                               struct hammer_ioc_prune *prune);
+static int hammer_ioc_gethistory(hammer_inode_t ip,
+                               struct hammer_ioc_history *hist);
+
+int
+hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
+            struct ucred *cred)
+{
+       int error;
+
+       error = suser_cred(cred, PRISON_ROOT);
+
+       switch(com) {
+       case HAMMERIOC_PRUNE:
+               if (error == 0) {
+                       error = hammer_ioc_prune(ip,
+                                       (struct hammer_ioc_prune *)data);
+               }
+               break;
+       case HAMMERIOC_GETHISTORY:
+               error = hammer_ioc_gethistory(ip,
+                                       (struct hammer_ioc_history *)data);
+               break;
+       default:
+               error = EOPNOTSUPP;
+               break;
+       }
+       return (error);
+}
+
+/*
+ * Iterate through the specified range of object ids and remove any
+ * deleted records that fall entirely within a prune modulo.
+ */
+static int check_prune(struct hammer_ioc_prune *prune, hammer_btree_elm_t elm);
+
+static int
+hammer_ioc_prune(hammer_inode_t ip, struct hammer_ioc_prune *prune)
+{
+       struct hammer_cursor cursor;
+       hammer_btree_elm_t elm;
+       int error;
+
+       if (prune->nelms < 0 || prune->nelms > HAMMER_MAX_PRUNE_ELMS) {
+               return(EINVAL);
+       }
+       if (prune->beg_obj_id >= prune->end_obj_id) {
+               return(EINVAL);
+       }
+
+retry:
+       error = hammer_init_cursor_hmp(&cursor, NULL, ip->hmp);
+       if (error) {
+               hammer_done_cursor(&cursor);
+               return(error);
+       }
+       cursor.key_beg.obj_id = prune->cur_obj_id;
+       cursor.key_beg.key = prune->cur_key;
+       cursor.key_beg.create_tid = 1;
+       cursor.key_beg.delete_tid = 0;
+       cursor.key_beg.rec_type = HAMMER_MIN_RECTYPE;
+       cursor.key_beg.obj_type = 0;
+
+       cursor.key_end.obj_id = prune->end_obj_id;
+       cursor.key_end.key = HAMMER_MIN_KEY;
+       cursor.key_end.create_tid = 1;
+       cursor.key_end.delete_tid = 0;
+       cursor.key_end.rec_type = HAMMER_MIN_RECTYPE;
+       cursor.key_end.obj_type = 0;
+
+       cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE;
+
+       error = hammer_btree_first(&cursor);
+       while (error == 0) {
+               elm = &cursor.node->ondisk->elms[cursor.index];
+               if (check_prune(prune, elm) == 0) {
+                       if (hammer_debug_general & 0x0200) {
+                               kprintf("check %016llx %016llx: DELETE\n",
+                                       elm->base.obj_id, elm->base.key);
+                       }
+                       /*
+                        * NOTE: This can return EDEADLK
+                        */
+                       prune->cur_obj_id = elm->base.obj_id;
+                       prune->cur_key = elm->base.key;
+                       if (elm->base.rec_type == HAMMER_RECTYPE_DIRENTRY)
+                               ++prune->stat_dirrecords;
+                       error = hammer_delete_at_cursor(&cursor,
+                                                       &prune->stat_bytes);
+                       if (error == 0)
+                               ++prune->stat_rawrecords;
+                       else
+                               --prune->stat_dirrecords;
+               } else {
+                       cursor.flags |= HAMMER_CURSOR_ATEDISK;
+                       if (hammer_debug_general & 0x0100) {
+                               kprintf("check %016llx %016llx: SKIP\n",
+                                       elm->base.obj_id, elm->base.key);
+                       }
+               }
+               if (error == 0)
+                       error = hammer_btree_iterate(&cursor);
+       }
+       if (error == ENOENT)
+               error = 0;
+       hammer_done_cursor(&cursor);
+       if (error == EDEADLK)
+               goto retry;
+       return(error);
+}
+
+/*
+ * Check pruning list.  The list must be sorted in descending order.
+ */
+static int
+check_prune(struct hammer_ioc_prune *prune, hammer_btree_elm_t elm)
+{
+       struct hammer_ioc_prune_elm *scan;
+       int i;
+
+       if (elm->base.delete_tid == 0)
+               return(-1);
+       for (i = 0; i < prune->nelms; ++i) {
+               scan = &prune->elms[i];
+               if (elm->base.create_tid >= scan->end_tid ||
+                   elm->base.delete_tid >= scan->end_tid) {
+                       break;
+               }
+               if (elm->base.create_tid < scan->beg_tid)
+                       continue;
+               KKASSERT(elm->base.delete_tid >= scan->beg_tid);
+               if (elm->base.create_tid / scan->mod_tid ==
+                   elm->base.delete_tid / scan->mod_tid) {
+                       return(0);
+               }
+       }
+       return(-1);
+}
+
+/*
+ * Iterate through an object's inode or an object's records and record
+ * modification TIDs.
+ */
+static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
+                       hammer_btree_elm_t elm);
+
+static
+int
+hammer_ioc_gethistory(hammer_inode_t ip, struct hammer_ioc_history *hist)
+{
+       struct hammer_cursor cursor;
+       hammer_btree_elm_t elm;
+       int error;
+
+       /*
+        * Validate the structure and initialize for return.
+        */
+       if (hist->beg_tid > hist->end_tid)
+               return(EINVAL);
+       if (hist->flags & HAMMER_IOC_HISTORY_ATKEY) {
+               if (hist->key > hist->nxt_key)
+                       return(EINVAL);
+       }
+
+       hist->obj_id = ip->obj_id;
+       hist->count = 0;
+       hist->nxt_tid = hist->end_tid;
+       hist->flags &= ~HAMMER_IOC_HISTORY_NEXT_TID;
+       hist->flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY;
+       hist->flags &= ~HAMMER_IOC_HISTORY_EOF;
+       hist->flags &= ~HAMMER_IOC_HISTORY_UNSYNCED;
+       if ((ip->flags & HAMMER_INODE_MODMASK) & ~HAMMER_INODE_ITIMES)
+               hist->flags |= HAMMER_IOC_HISTORY_UNSYNCED;
+
+       /*
+        * Setup the cursor.  We can't handle undeletable records
+        * (create_tid of 0) at the moment.  A create_tid of 0 has
+        * a special meaning and cannot be specified in the cursor.
+        */
+       error = hammer_init_cursor_hmp(&cursor, &ip->cache[0], ip->hmp);
+       if (error) {
+               hammer_done_cursor(&cursor);
+               return(error);
+       }
+
+       cursor.key_beg.obj_id = hist->obj_id;
+       cursor.key_beg.create_tid = hist->beg_tid;
+       cursor.key_beg.delete_tid = 0;
+       cursor.key_beg.obj_type = 0;
+       if (cursor.key_beg.create_tid == HAMMER_MIN_TID)
+               cursor.key_beg.create_tid = 1;
+
+       cursor.key_end.obj_id = hist->obj_id;
+       cursor.key_end.create_tid = hist->end_tid;
+       cursor.key_end.delete_tid = 0;
+       cursor.key_end.obj_type = 0;
+
+       cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE;
+
+       if (hist->flags & HAMMER_IOC_HISTORY_ATKEY) {
+               /*
+                * key-range within the file.  For a regular file the
+                * on-disk key represents BASE+LEN, not BASE, so the
+                * first possible record containing the offset 'key'
+                * has an on-disk key of (key + 1).
+                */
+               cursor.key_beg.key = hist->key;
+               cursor.key_end.key = HAMMER_MAX_KEY;
+
+               switch(ip->ino_rec.base.base.obj_type) {
+               case HAMMER_OBJTYPE_REGFILE:
+                       ++cursor.key_beg.key;
+                       cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
+                       break;
+               case HAMMER_OBJTYPE_DIRECTORY:
+                       cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
+                       break;
+               case HAMMER_OBJTYPE_DBFILE:
+                       cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
+                       break;
+               default:
+                       error = EINVAL;
+                       break;
+               }
+               cursor.key_end.rec_type = cursor.key_beg.rec_type;
+       } else {
+               /*
+                * The inode itself.
+                */
+               cursor.key_beg.key = 0;
+               cursor.key_end.key = 0;
+               cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
+               cursor.key_end.rec_type = HAMMER_RECTYPE_INODE;
+       }
+
+       error = hammer_btree_first(&cursor);
+       while (error == 0) {
+               elm = &cursor.node->ondisk->elms[cursor.index];
+
+               add_history(ip, hist, elm);
+               if (hist->flags & (HAMMER_IOC_HISTORY_NEXT_TID |
+                                 HAMMER_IOC_HISTORY_NEXT_KEY |
+                                 HAMMER_IOC_HISTORY_EOF)) {
+                       break;
+               }
+               error = hammer_btree_iterate(&cursor);
+       }
+       if (error == ENOENT) {
+               hist->flags |= HAMMER_IOC_HISTORY_EOF;
+               error = 0;
+       }
+       hammer_done_cursor(&cursor);
+       return(error);
+}
+
+/*
+ * Add the scanned element to the ioctl return structure.  Some special
+ * casing is required for regular files to accomodate how data ranges are
+ * stored on-disk.
+ */
+static void
+add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
+           hammer_btree_elm_t elm)
+{
+       if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD)
+               return;
+       if ((hist->flags & HAMMER_IOC_HISTORY_ATKEY) &&
+           ip->ino_rec.base.base.obj_type == HAMMER_OBJTYPE_REGFILE) {
+               /*
+                * Adjust nxt_key
+                */
+               if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len &&
+                   hist->key < elm->leaf.base.key - elm->leaf.data_len) {
+                       hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len;
+               }
+               if (hist->nxt_key > elm->leaf.base.key)
+                       hist->nxt_key = elm->leaf.base.key;
+
+               /*
+                * Record is beyond MAXPHYS, there won't be any more records
+                * in the iteration covering the requested offset (key).
+                */
+               if (elm->leaf.base.key >= MAXPHYS &&
+                   elm->leaf.base.key - MAXPHYS > hist->key) {
+                       hist->flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
+               }
+
+               /*
+                * Data-range of record does not cover the key.
+                */
+               if (elm->leaf.base.key - elm->leaf.data_len > hist->key)
+                       return;
+
+       } else if (hist->flags & HAMMER_IOC_HISTORY_ATKEY) {
+               /*
+                * Adjust nxt_key
+                */
+               if (hist->nxt_key > elm->leaf.base.key &&
+                   hist->key < elm->leaf.base.key) {
+                       hist->nxt_key = elm->leaf.base.key;
+               }
+
+               /*
+                * Record is beyond the requested key.
+                */
+               if (elm->leaf.base.key > hist->key)
+                       hist->flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
+       }
+
+       /*
+        * Add create_tid if it is in-bounds.
+        */
+       if ((hist->count == 0 ||
+            elm->leaf.base.create_tid != hist->tid_ary[hist->count - 1]) &&
+           elm->leaf.base.create_tid >= hist->beg_tid &&
+           elm->leaf.base.create_tid < hist->end_tid) {
+               if (hist->count == HAMMER_MAX_HISTORY_ELMS) {
+                       hist->nxt_tid = elm->leaf.base.create_tid;
+                       hist->flags |= HAMMER_IOC_HISTORY_NEXT_TID;
+                       return;
+               }
+               hist->tid_ary[hist->count++] = elm->leaf.base.create_tid;
+       }
+
+       /*
+        * Add delete_tid if it is in-bounds.  Note that different portions
+        * of the history may have overlapping data ranges with different
+        * delete_tid's.  If this case occurs the delete_tid may match the
+        * create_tid of a following record.  XXX
+        *
+        *      [        ]
+        *            [     ]
+        */
+       if (elm->leaf.base.delete_tid &&
+           elm->leaf.base.delete_tid >= hist->beg_tid &&
+           elm->leaf.base.delete_tid < hist->end_tid) {
+               if (hist->count == HAMMER_MAX_HISTORY_ELMS) {
+                       hist->nxt_tid = elm->leaf.base.delete_tid;
+                       hist->flags |= HAMMER_IOC_HISTORY_NEXT_TID;
+                       return;
+               }
+               hist->tid_ary[hist->count++] = elm->leaf.base.delete_tid;
+       }
+}
+
diff --git a/sys/vfs/hammer/hammer_ioctl.h b/sys/vfs/hammer/hammer_ioctl.h
new file mode 100644 (file)
index 0000000..f22fb67
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.h,v 1.1 2008/02/04 08:33:17 dillon Exp $
+ */
+/*
+ * HAMMER ioctl's.  This file can be #included from userland
+ */
+
+#ifndef VFS_HAMMER_IOCTL_H_
+#define VFS_HAMMER_IOCTL_H_
+
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include "hammer_disk.h"
+
+/*
+ * HAMMERIOC_PRUNE
+ *
+ * beg/end TID ranges in the element array must be sorted in descending
+ * order, with the most recent (highest) range at elms[0].
+ */
+struct hammer_ioc_prune_elm {
+       hammer_tid_t    beg_tid;        /* starting tid */
+       hammer_tid_t    end_tid;        /* ending tid (non inclusive) */
+       hammer_tid_t    mod_tid;        /* modulo */
+};
+
+#define HAMMER_MAX_PRUNE_ELMS  64
+
+struct hammer_ioc_prune {
+       int             nelms;
+       int             reserved01;
+       int64_t         beg_obj_id;
+       int64_t         cur_obj_id;     /* initialize to beg_obj_id */
+       int64_t         cur_key;        /* initialize to HAMMER_MIN_KEY */
+       int64_t         end_obj_id;      /* (range-exclusive) */
+       int64_t         stat_scanrecords;/* number of records scanned */
+       int64_t         stat_rawrecords; /* number of raw records pruned */
+       int64_t         stat_dirrecords; /* number of dir records pruned */
+       int64_t         stat_bytes;      /* number of data bytes pruned */
+       int64_t         reserved02[8];
+       struct hammer_ioc_prune_elm elms[HAMMER_MAX_PRUNE_ELMS];
+};
+
+/*
+ * HAMMERIOC_GETHISTORY
+ *
+ * Retrieve an array of ordered transaction ids >= beg and < end indicating
+ * all changes made to the specified object's inode up to the
+ * maximum.
+ *
+ * If ATKEY is set the key field indicates a particular key within the
+ * inode to retrieve the history for.
+ *
+ * On return count is set to the number of elements returned, nxt_tid is
+ * set to the tid the caller should store in beg_tid to continue the
+ * iteration, and nxt_key is set to the nearest key boundary > key
+ * indicating the range key - nxt_key (nxt_key non-inclusive) the tid
+ * array represents.  Also obj_id is set to the object's inode number.
+ *
+ * nxt_key can be used to iterate the contents of a single file but should
+ * not be stored in key until all modifications at key have been retrieved.
+ * To work properly nxt_key should be initialized to HAMMER_MAX_KEY.
+ * Successive ioctl() calls will reduce nxt_key as appropriate so at the
+ * end of your iterating for 'key', key to nxt_key will represent the
+ * shortest range of keys that all returned TIDs apply to.
+ */
+
+#define HAMMER_MAX_HISTORY_ELMS        64
+
+struct hammer_ioc_history {
+       int64_t         obj_id;
+       hammer_tid_t    beg_tid;
+       hammer_tid_t    nxt_tid;
+       hammer_tid_t    end_tid;
+       int64_t         key;
+       int64_t         nxt_key;
+       int             count;
+       int             flags;
+       hammer_tid_t    tid_ary[HAMMER_MAX_HISTORY_ELMS];
+};
+
+#define HAMMER_IOC_HISTORY_ATKEY       0x0001
+#define HAMMER_IOC_HISTORY_NEXT_TID    0x0002  /* iterate via nxt_tid */
+#define HAMMER_IOC_HISTORY_NEXT_KEY    0x0004  /* iterate via nxt_key */
+#define HAMMER_IOC_HISTORY_EOF         0x0008  /* no more keys */
+#define HAMMER_IOC_HISTORY_UNSYNCED    0x0010  /* unsynced info in inode */
+
+#define HAMMERIOC_PRUNE                _IOWR('h',1,struct hammer_ioc_prune)
+#define HAMMERIOC_GETHISTORY   _IOWR('h',2,struct hammer_ioc_history)
+
+#endif
index 58a6fdd..80abe28 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_object.c,v 1.26 2008/01/25 21:50:56 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_object.c,v 1.27 2008/02/04 08:33:17 dillon Exp $
  */
 
 #include "hammer.h"
@@ -1421,50 +1421,7 @@ hammer_ip_delete_record(hammer_cursor_t cursor, hammer_tid_t tid)
         * delete the record.
         */
        if (error == 0 && (hmp->hflags & HMNT_NOHISTORY)) {
-               int32_t rec_offset;
-               int32_t data_offset;
-               int32_t data_len;
-               u_int8_t rec_type;
-               hammer_cluster_t cluster;
-
-               rec_offset = elm->leaf.rec_offset;
-               data_offset = elm->leaf.data_offset;
-               data_len = elm->leaf.data_len;
-               rec_type = elm->leaf.base.rec_type;
-               KKASSERT(rec_type == cursor->record->base.base.rec_type);
-
-               /*
-                * We must ref the cluster to prevent it from being
-                * freed prior to our freeing the last record.
-                */
-               cluster = cursor->node->cluster;
-               hammer_ref_cluster(cluster);
-
-               error = hammer_btree_delete(cursor);
-               if (error == 0) {
-                       /*
-                        * This forces a fixup for the iteration because
-                        * the cursor is now either sitting at the 'next'
-                        * element or sitting at the end of a leaf.
-                        */
-                       if ((cursor->flags & HAMMER_CURSOR_DISKEOF) == 0) {
-                               cursor->flags |= HAMMER_CURSOR_DELBTREE;
-                               cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
-                       }
-                       hammer_free_record(cluster, rec_offset, rec_type);
-                       if (data_offset && (data_offset - rec_offset < 0 ||
-                           data_offset - rec_offset >= HAMMER_RECORD_SIZE)) {
-                               hammer_free_data(cluster, data_offset,data_len);
-                       }
-               }
-#if 0
-               kprintf("hammer_ip_delete_record: %d:%d:%08x %08x/%d "
-                       "(%d remain in cluster)\n",
-                       cluster->volume->vol_no, cluster->clu_no,
-                       rec_offset, data_offset, data_len,
-                       cluster->ondisk->stat_records);
-#endif
-               hammer_rel_cluster(cluster, 0);
+               error = hammer_delete_at_cursor(cursor, NULL);
                if (error) {
                        panic("hammer_ip_delete_record: unable to physically delete the record!\n");
                        error = 0;
@@ -1473,6 +1430,62 @@ hammer_ip_delete_record(hammer_cursor_t cursor, hammer_tid_t tid)
        return(error);
 }
 
+int
+hammer_delete_at_cursor(hammer_cursor_t cursor, int64_t *stat_bytes)
+{
+       hammer_btree_elm_t elm;
+       int32_t rec_offset;
+       int32_t data_offset;
+       int32_t data_len;
+       u_int8_t rec_type;
+       hammer_cluster_t cluster;
+       int error;
+
+       elm = &cursor->node->ondisk->elms[cursor->index];
+       KKASSERT(elm->base.btype == HAMMER_BTREE_TYPE_RECORD);
+
+       rec_offset = elm->leaf.rec_offset;
+       data_offset = elm->leaf.data_offset;
+       data_len = elm->leaf.data_len;
+       rec_type = elm->leaf.base.rec_type;
+
+       /*
+        * We must ref the cluster to prevent it from being
+        * freed prior to our freeing the last record.
+        */
+       cluster = cursor->node->cluster;
+       hammer_ref_cluster(cluster);
+
+       error = hammer_btree_delete(cursor);
+       if (error == 0) {
+               /*
+                * This forces a fixup for the iteration because
+                * the cursor is now either sitting at the 'next'
+                * element or sitting at the end of a leaf.
+                */
+               if ((cursor->flags & HAMMER_CURSOR_DISKEOF) == 0) {
+                       cursor->flags |= HAMMER_CURSOR_DELBTREE;
+                       cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
+               }
+               hammer_free_record(cluster, rec_offset, rec_type);
+               if (data_offset && (data_offset - rec_offset < 0 ||
+                   data_offset - rec_offset >= HAMMER_RECORD_SIZE)) {
+                       hammer_free_data(cluster, data_offset,data_len);
+                       if (stat_bytes)
+                               *stat_bytes += data_len;
+               }
+       }
+#if 0
+       kprintf("hammer_delete_at_cursor: %d:%d:%08x %08x/%d "
+               "(%d remain in cluster)\n",
+               cluster->volume->vol_no, cluster->clu_no,
+               rec_offset, data_offset, data_len,
+               cluster->ondisk->stat_records);
+#endif
+       hammer_rel_cluster(cluster, 0);
+       return (error);
+}
+
 /*
  * Determine whether a directory is empty or not.  Returns 0 if the directory
  * is empty, ENOTEMPTY if it isn't, plus other possible errors.
index fb38c2d..d17dc2e 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_subs.c,v 1.12 2008/01/18 07:02:41 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_subs.c,v 1.13 2008/02/04 08:33:17 dillon Exp $
  */
 /*
  * HAMMER structural locking
@@ -334,8 +334,12 @@ hammer_tid_t
 hammer_str_to_tid(const char *str)
 {
        hammer_tid_t tid;
+       int len = strlen(str);
 
-       tid = strtoq(str, NULL, 16) * 1000000000LL;
+       if (len > 10)
+               tid = strtouq(str, NULL, 0);                    /* full TID */
+       else
+               tid = strtouq(str, NULL, 0) * 1000000000LL;     /* time_t */
        return(tid);
 }
 
index 64891c3..5f3e9b4 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_vnops.c,v 1.24 2008/01/25 10:36:04 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_vnops.c,v 1.25 2008/02/04 08:33:17 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -77,6 +77,7 @@ static int hammer_vop_setattr(struct vop_setattr_args *);
 static int hammer_vop_strategy(struct vop_strategy_args *);
 static int hammer_vop_nsymlink(struct vop_nsymlink_args *);
 static int hammer_vop_nwhiteout(struct vop_nwhiteout_args *);
+static int hammer_vop_ioctl(struct vop_ioctl_args *);
 
 static int hammer_vop_fifoclose (struct vop_close_args *);
 static int hammer_vop_fiforead (struct vop_read_args *);
@@ -116,7 +117,8 @@ struct vop_ops hammer_vnode_vops = {
        .vop_setattr =          hammer_vop_setattr,
        .vop_strategy =         hammer_vop_strategy,
        .vop_nsymlink =         hammer_vop_nsymlink,
-       .vop_nwhiteout =        hammer_vop_nwhiteout
+       .vop_nwhiteout =        hammer_vop_nwhiteout,
+       .vop_ioctl =            hammer_vop_ioctl
 };
 
 struct vop_ops hammer_spec_vops = {
@@ -581,7 +583,6 @@ hammer_vop_nresolve(struct vop_nresolve_args *ap)
        for (i = 0; i < nlen; ++i) {
                if (ncp->nc_name[i] == '@' && ncp->nc_name[i+1] == '@') {
                        asof = hammer_str_to_tid(ncp->nc_name + i + 2);
-                       kprintf("ASOF %016llx\n", asof);
                        flags |= HAMMER_INODE_RO;
                        break;
                }
@@ -618,7 +619,7 @@ hammer_vop_nresolve(struct vop_nresolve_args *ap)
         */
        namekey = hammer_directory_namekey(ncp->nc_name, nlen);
 
-       hammer_init_cursor_hmp(&cursor, &dip->cache[0], dip->hmp);
+       error = hammer_init_cursor_hmp(&cursor, &dip->cache[0], dip->hmp);
         cursor.key_beg.obj_id = dip->obj_id;
        cursor.key_beg.key = namekey;
         cursor.key_beg.create_tid = 0;
@@ -638,7 +639,9 @@ hammer_vop_nresolve(struct vop_nresolve_args *ap)
         * The hammer_ip_*() functions merge in-memory records with on-disk
         * records for the purposes of the search.
         */
-       error = hammer_ip_first(&cursor, dip);
+       if (error == 0)
+               error = hammer_ip_first(&cursor, dip);
+
        rec = NULL;
        obj_id = 0;
 
@@ -1483,6 +1486,19 @@ hammer_vop_nwhiteout(struct vop_nwhiteout_args *ap)
        return(hammer_dounlink(ap->a_nch, ap->a_dvp, ap->a_cred, ap->a_flags));
 }
 
+/*
+ * hammer_vop_ioctl { vp, command, data, fflag, cred }
+ */
+static
+int
+hammer_vop_ioctl(struct vop_ioctl_args *ap)
+{
+       struct hammer_inode *ip = ap->a_vp->v_data;
+
+       return(hammer_ioctl(ip, ap->a_command, ap->a_data,
+                           ap->a_fflag, ap->a_cred));
+}
+
 /*
  * hammer_vop_strategy { vp, bio }
  *