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