diff filesystem
Oskar Liljeblad
oskar at osk.mine.nu
Fri Feb 22 22:57:57 UTC 2002
I got tired of waiting when browsing kernel patch files
using the patchfs in mc, so wrote this patch. It adds
a new vfs called difffs (with prefix udiff), replacing
the patchfs bash script in extfs. The main (and only)
difference is that read a file in the diff takes O(1)
worst case, compared to O(n) worst case (n = size of
the whole diff file) in patchfs. (The kernel 2.5.5
5MB patch takes 3-4 seconds to load, and waiting that
long every time you want to view a single file inside
it is unacceptable.)
This patch is against the 2002-02-22-06 snapshot.
This patch does not replace patchfs with udiff
in the default configuration files.
Much of this patch was copied from tar.c, so I consider
the copyright owner to be same as for that file.
Let me know what you decide to do with this patch.
Oskar Liljeblad (oskar at osk.mine.nu)
-------------- next part --------------
diff -ruN mc-4.5.99a/vfs/Makefile.am mc-4.5.99a-oskar/vfs/Makefile.am
--- mc-4.5.99a/vfs/Makefile.am Wed Feb 20 07:15:00 2002
+++ mc-4.5.99a-oskar/vfs/Makefile.am Fri Feb 22 23:04:46 2002
@@ -13,6 +13,7 @@
local.c \
names.c \
tar.c \
+ diff.c \
sfs.c \
vfs.c
diff -ruN mc-4.5.99a/vfs/diff.c mc-4.5.99a-oskar/vfs/diff.c
--- mc-4.5.99a/vfs/diff.c Thu Jan 1 01:00:00 1970
+++ mc-4.5.99a-oskar/vfs/diff.c Fri Feb 22 23:06:27 2002
@@ -0,0 +1,438 @@
+/* Virtual File System: Diff file system.
+ Copyright (C) 1995 The Free Software Foundation
+
+ Written by: 2002 Oskar Liljeblad
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License
+ as published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Namespace: vfs_difffs_ops */
+
+#include <config.h>
+#include <errno.h>
+#include "utilvfs.h" /* for message_2s, message_1s */
+#include "xdirentry.h"
+#include "../src/dialog.h" /* for MSG_ERROR */
+#include <glib.h>
+
+#define MIN_CHUNK 64
+#define READBUFSIZE 32768
+
+struct readbuf {
+ int fd;
+ char *buf;
+ size_t pos;
+ size_t len;
+};
+
+static void *diff_super_check(vfs *me, char *archive_name, char *op)
+{
+ static struct stat stat_buf;
+ if (mc_stat(archive_name, &stat_buf))
+ return NULL;
+ return &stat_buf;
+}
+
+static int diff_super_same(vfs *me, struct vfs_s_super *parc, char *archive_name, char *op, void *cookie)
+{
+ struct stat *archive_stat = cookie; /* stat of main archive */
+
+ if (strcmp(parc->name, archive_name) != 0)
+ return 0;
+
+ if (vfs_uid && (!(archive_stat->st_mode & 0004))
+ && ((archive_stat->st_gid != vfs_gid) || !(archive_stat->st_mode & 0040))
+ && ((archive_stat->st_uid != vfs_uid) || !(archive_stat->st_mode & 0400)))
+ return 0;
+
+ /* Has the cached archive been changed on the disk? */
+ if (parc->u.tar.tarstat.st_mtime < archive_stat->st_mtime) { /* Yes, reload! */
+ (*vfs_tarfs_ops.free)((vfsid) parc);
+ vfs_rmstamp (&vfs_tarfs_ops, (vfsid) parc, 0);
+ return 2;
+ }
+
+ /* Hasn't been modified, give it a new timeout */
+ vfs_stamp (&vfs_tarfs_ops, (vfsid) parc);
+ return 1;
+}
+
+static int diff_fh_open(vfs *me, vfs_s_fh *fh, int flags, int mode)
+{
+ if ((flags & O_ACCMODE) != O_RDONLY)
+ ERRNOR (EROFS, -1);
+ return 0;
+}
+
+static void diff_free_archive(vfs *me, vfs_s_super *archive)
+{
+ if (archive->u.tar.fd != -1)
+ mc_close(archive->u.tar.fd);
+ if (archive->u.diff.list_fd != -1)
+ close(archive->u.diff.list_fd);
+ if (archive->u.diff.list_name != NULL) {
+ unlink(archive->u.diff.list_name); /* ignore error? */
+ g_free(archive->u.diff.list_name);
+ }
+}
+
+static int diff_read(void *fh, char *buffer, int count)
+{
+ off_t begin = FH->ino->u.tar.data_offset;
+ int fd = FH_SUPER->u.tar.fd;
+ vfs *me = FH_SUPER->me;
+
+ if (begin == -2) {
+ if (lseek(FH_SUPER->u.diff.list_fd, FH->pos, SEEK_SET) < 0)
+ ERRNOR(errno, -1);
+ if ((count = read(FH_SUPER->u.diff.list_fd, buffer, count)) < 0)
+ ERRNOR(errno, -1);
+ FH->pos += count;
+ return count;
+ }
+
+ if (mc_lseek(fd, begin + FH->pos, SEEK_SET) != begin + FH->pos)
+ ERRNOR(EIO, -1);
+
+ count = MIN(count, FH->ino->st.st_size - FH->pos);
+
+ if ((count = mc_read(fd, buffer, count)) == -1)
+ ERRNOR(errno, -1);
+
+ FH->pos += count;
+ return count;
+}
+
+static int diff_ungetlocalcopy (vfs *me, char *path, char *local, int has_changed)
+{
+ /* We do just nothing. (We are read only and do not need to free local,
+ * since it will be freed when diff archive will be freed
+ * We have to report error if file has changed.
+ */
+ ERRNOR(EROFS, -has_changed);
+}
+
+static void rb_free(struct readbuf *rb)
+{
+ g_free(rb->buf);
+}
+
+static void rb_init(struct readbuf *rb, int fd)
+{
+ rb->fd = fd;
+ rb->len = 0;
+ rb->pos = 0;
+ rb->buf = g_malloc(READBUFSIZE);
+}
+
+static ssize_t rb_readchar(struct readbuf *rb, char *buf)
+{
+ if (rb->pos >= rb->len) {
+ rb->len = 0;
+ do {
+ int rc = mc_read(rb->fd, rb->buf+rb->len, READBUFSIZE-rb->len);
+ if (rc < 0)
+ return rc;
+ if (rc == 0) {
+ if (rb->len == 0)
+ return 0;
+ break;
+ }
+ rb->len += rc;
+ } while (rb->len < READBUFSIZE);
+ rb->pos = 0;
+ }
+ *buf = rb->buf[rb->pos++];
+ return 1;
+}
+
+static ssize_t rb_getdelim(struct readbuf *rb, char **lineptr, size_t *n, int delim)
+{
+ int nchars_avail;
+ char *read_pos;
+
+ if (!*lineptr) {
+ *n = MIN_CHUNK;
+ *lineptr = g_malloc(*n);
+ }
+ nchars_avail = *n;
+ read_pos = *lineptr;
+
+ for (;;) {
+ char c;
+ int rc;
+
+ rc = rb_readchar(rb, &c);
+
+ if (nchars_avail < 2) {
+ if (*n > MIN_CHUNK)
+ *n *= 2;
+ else
+ *n += MIN_CHUNK;
+ nchars_avail = *n + *lineptr - read_pos;
+ *lineptr = g_realloc (*lineptr, *n);
+ read_pos = *n - nchars_avail + *lineptr;
+ }
+
+ if (rc < 0)
+ return -1;
+ if (rc == 0) {
+ if (read_pos == *lineptr)
+ return 0;
+ break;
+ }
+
+ *read_pos++ = c;
+ nchars_avail--;
+
+ if (c == delim)
+ break;
+ }
+
+ *read_pos = 0;
+ return read_pos - (*lineptr);
+}
+
+static int diff_open_archive(vfs *me, vfs_s_super *archive, char *name, char *op)
+{
+ int diff_fd, list_fd, type, result;
+ long size, list_size = 0;
+ long offset;
+ mode_t mode;
+ struct vfs_s_inode *root;
+ struct stat std_st;
+ struct readbuf rb;
+ char *linebuf = NULL;
+ size_t linesize = 0;
+ ssize_t start_offset = -1;
+ struct vfs_s_inode *parent = NULL;
+ char *filename = NULL;
+ struct vfs_s_entry *entry;
+ struct vfs_s_inode *inode;
+
+ diff_fd = mc_open(name, O_RDONLY);
+ if (diff_fd == -1) {
+ message_3s(1, MSG_ERROR, _("Cannot open \"%s\"\n%s"), name, g_strerror(mc_errno));
+ ERRNOR(ENOENT, -1);
+ }
+
+ archive->name = g_strdup(name);
+ mc_stat(name, &(archive->u.tar.tarstat));
+ archive->u.tar.fd = -1;
+
+ /* Find out the method to handle this diff file */
+ size = is_gunzipable(diff_fd, &type);
+ mc_lseek(diff_fd, 0, SEEK_SET);
+ if (size > 0) {
+ char *s;
+ mc_close(diff_fd);
+ s = g_strconcat(archive->name, decompress_extension(type), NULL );
+ diff_fd = mc_open(s, O_RDONLY);
+ if (diff_fd == -1)
+ message_3s(1, MSG_ERROR, _("Cannot open \"%s\"\n%s"), s, g_strerror(mc_errno));
+ g_free(s);
+ if (diff_fd == -1)
+ ERRNOR(ENOENT, -1);
+ }
+
+ archive->u.tar.fd = diff_fd;
+ mode = archive->u.tar.tarstat.st_mode & 07777;
+ if (mode & 0400) mode |= 0100;
+ if (mode & 0040) mode |= 0010;
+ if (mode & 0004) mode |= 0001;
+ mode |= S_IFDIR;
+
+ root = vfs_s_new_inode (me, archive, &archive->u.tar.tarstat);
+ root->st.st_mode = mode;
+ root->u.tar.data_offset = -1;
+ root->st.st_nlink++;
+ root->st.st_dev = MEDATA->rdev++;
+ vfs_s_add_dots(me, root, NULL);
+ archive->root = root;
+
+ std_st.st_mode = 0444 | S_IFREG;
+ std_st.st_uid = 0;
+ std_st.st_gid = 0;
+ std_st.st_rdev = 0;
+ std_st.st_ctime = time(NULL); /* ignore errors */
+ std_st.st_atime = std_st.st_ctime;
+ std_st.st_mtime = std_st.st_ctime;
+
+ /* Create temporary file for FILELIST */
+ list_fd = mc_mkstemps(&archive->u.diff.list_name, "sfs", NULL);
+ if (list_fd == -1) {
+ message_1s(1, MSG_ERROR, _("Cannot create temporary file"));
+ return -1; /* handle better? */
+ }
+ archive->u.diff.list_fd = list_fd;
+
+ rb_init(&rb, diff_fd);
+ offset = 0;
+ result = -1;
+
+ for (;;) {
+ ssize_t linelen;
+
+ linelen = rb_getdelim(&rb, &linebuf, &linesize, '\n');
+ if (linelen < 0) {
+ me->verrno = errno;
+ break;
+ }
+ offset += linelen;
+
+ if (linelen == 0 || strncmp(linebuf, "diff ", 5) == 0) {
+ char *q;
+ int c;
+
+ if (start_offset != -1) {
+ std_st.st_size = offset - start_offset - linelen;
+
+ inode = vfs_s_new_inode(me, archive, &std_st);
+ inode->u.tar.data_offset = start_offset;
+
+ entry = vfs_s_new_entry(me, filename, inode);
+ vfs_s_insert_entry(me, parent, entry);
+ g_free(filename);
+ filename = NULL;
+ }
+ if (linelen == 0) {
+ result = 0;
+ break;
+ }
+
+ start_offset = offset - linelen;
+
+ for (c = linelen-1; isspace(linebuf[c]); c--);
+ linebuf[c+1] = 0;
+
+ q = strrchr(linebuf, ' ');
+ if (q == NULL) {
+ message_1s(1, MSG_ERROR, _("Inconsistent diff file"));
+ break;
+ }
+
+ write(list_fd, q+1, linebuf + c - q);
+ write(list_fd, "\n", 1);
+ list_size += linebuf + c - q + 1;
+
+ filename = strrchr(q+1, '/');
+ if (filename == NULL) {
+ filename = q+1;
+ q = linebuf+linelen; /* "" */
+ } else {
+ *(filename++) = 0;
+ q = q+1;
+ }
+
+ filename = g_strdup(filename);
+
+ parent = vfs_s_find_inode(me, archive->root, q, LINK_NO_FOLLOW, FL_MKDIR);
+ if (parent == NULL) {
+ message_1s(1, MSG_ERROR, _("Inconsistent diff file"));
+ break;
+ }
+ }
+ }
+
+ std_st.st_size = list_size;
+ inode = vfs_s_new_inode(me, archive, &std_st);
+ /* -2 is the magic code that identifies this special file */
+ inode->u.tar.data_offset = -2;
+ entry = vfs_s_new_entry(me, "FILELIST", inode);
+ vfs_s_insert_entry(me, root, entry);
+
+ rb_free(&rb);
+ g_free(filename);
+ g_free(linebuf);
+ return result;
+}
+
+static struct vfs_s_data diff_data = {
+ NULL, /* supers */
+ 0, /* inode_counter */
+ 0, /* rdev */
+ NULL, /* logfile */
+
+ NULL, /* init_inode */
+ NULL, /* free_inode */
+ NULL, /* init_entry */
+
+ diff_super_check, /* archive_check */
+ diff_super_same, /* archive_same */
+ diff_open_archive,
+ diff_free_archive,
+
+ diff_fh_open,
+ NULL, /* fh_close */
+
+ vfs_s_find_entry_tree,
+ NULL, /* dir_load */
+ NULL /* file_store */
+};
+
+vfs vfs_difffs_ops = {
+ NULL, /* next */
+ "difffs",
+ 0, /* flags */
+ "udiff",
+ &diff_data,
+ 0, /* errno */
+ NULL, /* init */
+ NULL, /* done */
+ vfs_s_fill_names,
+ NULL, /* which */
+
+ vfs_s_open,
+ vfs_s_close,
+ diff_read,
+ NULL, /* write */
+
+ vfs_s_opendir,
+ vfs_s_readdir,
+ vfs_s_closedir,
+ vfs_s_telldir,
+ vfs_s_seekdir,
+
+ vfs_s_stat,
+ vfs_s_lstat,
+ vfs_s_fstat,
+
+ NULL, /* chmod */
+ NULL, /* chown */
+ NULL, /* utime */
+
+ vfs_s_readlink,
+ NULL, /* symlink */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* rename */
+ vfs_s_chdir,
+ vfs_s_ferrno,
+ vfs_s_lseek,
+ NULL, /* mknod */
+
+ vfs_s_getid,
+ vfs_s_nothingisopen,
+ vfs_s_free,
+
+ vfs_s_getlocalcopy,
+ diff_ungetlocalcopy,
+
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* ctl */
+ NULL /* setctl */
+
+MMAPNULL
+};
diff -ruN mc-4.5.99a/vfs/vfs.h mc-4.5.99a-oskar/vfs/vfs.h
--- mc-4.5.99a/vfs/vfs.h Sun Feb 10 03:26:19 2002
+++ mc-4.5.99a-oskar/vfs/vfs.h Fri Feb 22 23:12:07 2002
@@ -108,6 +108,7 @@
extern vfs vfs_nil_ops;
extern vfs vfs_tarfs_ops;
extern vfs vfs_cpiofs_ops;
+ extern vfs vfs_difffs_ops;
extern vfs vfs_ftpfs_ops;
extern vfs vfs_smbfs_ops;
diff -ruN mc-4.5.99a/vfs/xdirentry.h mc-4.5.99a-oskar/vfs/xdirentry.h
--- mc-4.5.99a/vfs/xdirentry.h Fri Aug 3 12:15:17 2001
+++ mc-4.5.99a-oskar/vfs/xdirentry.h Fri Feb 22 23:12:47 2002
@@ -88,6 +88,12 @@
struct stat tarstat;
} tar;
struct {
+ int fd;
+ struct stat tarstat;
+ int list_fd;
+ char *list_name;
+ } diff;
+ struct {
int sockr, sockw;
char *home, *cwdir;
char *host, *user;
diff -ruN mc-4.5.99a/vfs/vfs.c mc-4.5.99a-oskar/vfs/vfs.c
--- mc-4.5.99a/vfs/vfs.c Sun Feb 10 03:26:19 2002
+++ mc-4.5.99a-oskar/vfs/vfs.c Fri Feb 22 23:19:20 2002
@@ -1275,6 +1275,7 @@
vfs_register (&vfs_sfs_ops);
vfs_register (&vfs_tarfs_ops);
vfs_register (&vfs_cpiofs_ops);
+ vfs_register (&vfs_difffs_ops);
#ifdef USE_EXT2FSLIB
vfs_register (&vfs_undelfs_ops);
More information about the mc-devel
mailing list