diff -Naur linux-2.6.16.13/arch/i386/kernel/sys_i386.c linux-2.6.16.13-fschange/arch/i386/kernel/sys_i386.c
--- linux-2.6.16.13/arch/i386/kernel/sys_i386.c	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/arch/i386/kernel/sys_i386.c	2006-05-02 23:52:29.000000000 -0400
@@ -6,6 +6,7 @@
  * platform.
  */
 
+#include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -48,6 +49,7 @@
 {
 	int error = -EBADF;
 	struct file * file = NULL;
+	unsigned long start_offset;
 
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	if (!(flags & MAP_ANONYMOUS)) {
@@ -60,6 +62,14 @@
 	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 	up_write(&current->mm->mmap_sem);
 
+	if ((!IS_ERR((void*)error)) && (file) && (prot & PROT_WRITE) && (!(flags & MAP_PRIVATE))) {
+		start_offset = (pgoff << PAGE_SHIFT);
+		if ((start_offset < file->f_start_of_change) || (file->f_end_of_change == 0))
+			file->f_start_of_change = start_offset;
+		if (start_offset + len > file->f_end_of_change)
+			file->f_end_of_change = start_offset + len;
+	}
+
 	if (file)
 		fput(file);
 out:
diff -Naur linux-2.6.16.13/arch/ia64/kernel/sys_ia64.c linux-2.6.16.13-fschange/arch/ia64/kernel/sys_ia64.c
--- linux-2.6.16.13/arch/ia64/kernel/sys_ia64.c	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/arch/ia64/kernel/sys_ia64.c	2006-05-02 23:52:29.000000000 -0400
@@ -6,6 +6,7 @@
  *	David Mosberger-Tang <davidm@hpl.hp.com>
  */
 #include <linux/config.h>
+#include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
@@ -204,6 +205,14 @@
 	addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 	up_write(&current->mm->mmap_sem);
 
+	if ((!IS_ERR(error)) && (file) && (prot & PROT_WRITE) && (!(flags & MAP_PRIVATE))) {
+		start_offset = (pgoff << PAGE_SHIFT);
+		if ((start_offset < file->f_start_of_change) || (file->f_end_of_change == 0))
+			file->f_start_of_change = start_offset;
+		if (start_offset + len > file->f_end_of_change)
+			file->f_end_of_change = start_offset + len;
+	}
+
 out:	if (file)
 		fput(file);
 	return addr;
diff -Naur linux-2.6.16.13/arch/x86_64/kernel/sys_x86_64.c linux-2.6.16.13-fschange/arch/x86_64/kernel/sys_x86_64.c
--- linux-2.6.16.13/arch/x86_64/kernel/sys_x86_64.c	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/arch/x86_64/kernel/sys_x86_64.c	2006-05-02 23:52:29.000000000 -0400
@@ -2,6 +2,7 @@
  * linux/arch/x86_64/kernel/sys_x86_64.c
  */
 
+#include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/syscalls.h>
@@ -42,6 +43,7 @@
 {
 	long error;
 	struct file * file;
+	unsigned long start_offset;
 
 	error = -EINVAL;
 	if (off & ~PAGE_MASK)
@@ -59,6 +61,14 @@
 	error = do_mmap_pgoff(file, addr, len, prot, flags, off >> PAGE_SHIFT);
 	up_write(&current->mm->mmap_sem);
 
+	if ((!IS_ERR((void*)error)) && (file) && (prot & PROT_WRITE) && (!(flags & MAP_PRIVATE))) {
+		start_offset = off;
+		if ((start_offset < file->f_start_of_change) || (file->f_end_of_change == 0))
+			file->f_start_of_change = start_offset;
+		if (start_offset + len > file->f_end_of_change)
+			file->f_end_of_change = start_offset + len;
+	}
+
 	if (file)
 		fput(file);
 out:
diff -Naur linux-2.6.16.13/fs/file_table.c linux-2.6.16.13-fschange/fs/file_table.c
--- linux-2.6.16.13/fs/file_table.c	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/fs/file_table.c	2006-05-02 23:52:29.000000000 -0400
@@ -117,6 +117,12 @@
 	f->f_uid = current->fsuid;
 	f->f_gid = current->fsgid;
 	rwlock_init(&f->f_owner.lock);
+
+	// fschange initialization
+	f->f_start_of_change = 0;
+	f->f_end_of_change = 0;
+	f->f_name = NULL;
+
 	/* f->f_version: 0 */
 	INIT_LIST_HEAD(&f->f_u.fu_list);
 	return f;
diff -Naur linux-2.6.16.13/fs/fschange.c linux-2.6.16.13-fschange/fs/fschange.c
--- linux-2.6.16.13/fs/fschange.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.16.13-fschange/fs/fschange.c	2006-05-02 23:52:29.000000000 -0400
@@ -0,0 +1,105 @@
+/**
+ *  linux/fs/fschange.c
+ *
+ *  Copyright (C) 2005 Stefan Buettcher
+ *
+ * The functions defined in this file are used by the various parts of the
+ * kernel that can cause file system changes to interact with the fschange
+ * kernel module. The module itself also uses them (fschange_set_notifier)
+ * to register with the kernel when it is loaded.
+ **/
+
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vfs.h>
+#include <linux/fs.h>
+#include <linux/fschange.h>
+#include <asm/unistd.h>
+
+
+/**
+ * Returns the full path of the file given by "rel_filename", using the information
+ * about the current file system state stored in "fs".
+ **/
+char *fschange_get_full_filename(const char *rel_filename, struct fs_struct *fs)
+{
+	if (rel_filename == NULL)
+		return NULL;
+	else if (rel_filename[0] == '/') {
+		char *result = (char*)kmalloc(strlen(rel_filename) + 1, GFP_KERNEL);
+		strcpy(result, rel_filename);
+		return result;
+	}
+	else if (rel_filename[0] == 0) {
+		char *dirname_buffer = (char*)kmalloc(1024, GFP_KERNEL);
+		char *dirname = d_path(fs->pwd, fs->pwdmnt, dirname_buffer, 1023);
+		if (IS_ERR(dirname)) {
+			kfree(dirname_buffer);
+			return NULL;
+		}
+		else {
+			char *result = (char*)kmalloc(strlen(dirname) + 1, GFP_KERNEL);
+			strcpy(result, dirname);
+			kfree(dirname_buffer);
+			return result;
+		}
+	}
+	else {
+		char *dirname_buffer = (char*)kmalloc(1024, GFP_KERNEL);
+		char *dirname = d_path(fs->pwd, fs->pwdmnt, dirname_buffer, 1023);
+		if (IS_ERR(dirname)) {
+			kfree(dirname_buffer);
+			return NULL;
+		}
+		else {
+			int dirname_len = strlen(dirname);
+			int filename_len = strlen(rel_filename);
+			char *result = (char*)kmalloc(dirname_len + filename_len + 2, GFP_KERNEL);
+			strcpy(result, dirname);
+			if ((dirname_len == 0) || (dirname[dirname_len - 1] != '/'))
+				strcat(result, "/");
+			kfree(dirname_buffer);
+			strcat(result, rel_filename);
+			return result;
+		}
+	}
+} // end of fschange_get_full_filename(char*, struct fs_struct*)
+
+EXPORT_SYMBOL(fschange_get_full_filename);
+
+
+/**
+ * If non-NULL, this function is called by various parts of the kernel to send
+ * messages to the fschange kernel module. The first parameter is the message
+ * type, as defined in fschange.h, followed by old file name, new file name,
+ * start of change, end of change.
+ **/
+void (*fschange_notifier)(int, const char*, const char*, loff_t, loff_t) = NULL;
+
+EXPORT_SYMBOL(fschange_notifier);
+
+
+/** Sets "fschange_notifier" to the function given by "new_notifier". **/
+void fschange_set_notifier(void (*new_notifier)(int, const char*, const char*, loff_t, loff_t))
+{
+	        fschange_notifier = new_notifier;
+} // end of fschange_set_notifier(...)
+                                                                                                              
+EXPORT_SYMBOL(fschange_set_notifier);
+
+
+void fschange_notify(int messageType, const char *oldFile, const char *newFile,
+		loff_t from, loff_t to)
+{
+	if ((fschange_notifier == NULL) || (messageType <= 0))
+		return;
+	fschange_notifier(messageType, oldFile, newFile, from, to);
+} // end of fschange_notify(int, const char*, const char*, loff_t, loff_t)
+
+EXPORT_SYMBOL(fschange_notify);
+
+
diff -Naur linux-2.6.16.13/fs/fschange_module.c linux-2.6.16.13-fschange/fs/fschange_module.c
--- linux-2.6.16.13/fs/fschange_module.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.16.13-fschange/fs/fschange_module.c	2006-05-02 23:52:29.000000000 -0400
@@ -0,0 +1,674 @@
+/*****
+ * This kernel module helps us keep track of file changes. In order to minimize
+ * the changes to the Linux kernel, we insert only a few lines into a couple of
+ * fs-related files (and add fs/fschange.c). The main work is done inside this
+ * module.
+ *
+ * author: Stefan Buettcher
+ * created: 2004-10-06
+ * changed: 2006-01-09
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *****/
+
+
+#include <asm/semaphore.h>
+#include <linux/compiler.h>
+#include <linux/config.h>
+#include <linux/fschange.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/tty.h>
+#include <linux/vermagic.h>
+#include <linux/vmalloc.h>
+
+
+#define EXPORT_SYMTAB
+
+
+MODULE_AUTHOR("Stefan Buettcher <sbuettch@uwaterloo.ca>");
+MODULE_DESCRIPTION("fschange -- File system notification service for Linux.");
+MODULE_LICENSE("GPL");
+MODULE_INFO(vermagic, VERMAGIC_STRING); 
+
+
+#define PROCFILE_NAME   "fschange"
+#define OK_MESSAGE    "fschange module successfully loaded into kernel"
+#define ERROR_MESSAGE   "unable to load fschange module into kernel -- proc file creation failed"
+
+#define MIN(a, b) (a < b ? a : b)
+#define MAX(a, b) (a > b ? a : b)
+
+
+/**
+ * String representations for all possible file system events supported by
+ * fschange. Different events come with different parameters. I suggest you do
+ * a couple of experiments reading from the proc file in order to find out the
+ * exact syntax of every event.
+ * The ERROR event is not a file system event. It is used to indicate that the
+ * internal buffers are full and the module therefore was unable to catch all
+ * events that occurred.
+ **/
+static const char *event_strings[16] = {
+	NULL,
+	"MOUNT", "UMOUNT", "UMOUNT_REQ", "WRITE", "TRUNCATE", "RENAME",
+	"UNLINK", "CHMOD", "CHOWN", "CREATE", "MKDIR", "RMDIR",
+	"ERROR",
+	NULL
+};
+
+static const int MIN_MESSAGE_TYPE = 1;
+static const int MAX_MESSAGE_TYPE = 12;
+
+
+/**
+ * Communication with user land is done via the proc file /proc/changed_files.
+ * Reading and writing is allowed. The proc file is accessed exclusively by the
+ * fschanged (filesystem change daemon), which informs the fsindexd about
+ * what's going on.
+ **/
+static struct proc_dir_entry* proc_file;
+
+
+/** Handling read requests to the proc file. **/
+static int read_proc_file(char* page, char** start, off_t off, int count, int* eof, void* data);
+
+
+/** Handling write requests to the proc file. **/
+static int write_proc_file(struct file* file, const char* buffer, unsigned long count, void* data);
+
+
+/** Events longer than this will not be processed. **/
+static const int MAX_EVENT_LENGTH = 2048;
+
+/** Minimum number of characters transferred in a single read operation. **/
+static const int MIN_READ_SIZE = 16;
+
+
+/**
+ * File system events are stored in various in-memory buffers. There are two
+ * uncompressed character buffers, read_buffer and write_buffer. read_buffer
+ * represents the buffer that is used to serve userland read requests, whereas
+ * write_buffer is used to store new events reported by the kernel hooks.
+ **/
+
+#define UNCOMPRESSED_BUFFER_SIZE (32768)
+
+/** Buffer of size UNCOMPRESSED_BUFFER_SIZE. **/
+static char *read_buffer;
+
+/**
+ * Current position (read pointer) in the read_buffer. If
+ * read_buffer_pos equals read_buffer_size, the buffer is empty.
+ **/
+static int read_buffer_pos;
+static int read_buffer_size;
+
+/** Buffer of size UNCOMPRESSED_BUFFER_SIZE. **/
+static char *write_buffer;
+
+/**
+ * Current position (write pointer) in the write_buffer. If write_buffer_size
+ * equals UNCOMPRESSED_BUFFER_SIZE, the buffer is full.
+ **/
+static int write_buffer_size;
+
+/** Temporary buffer for compressing event buffers. **/
+static char *temp_buffer;
+
+/**
+ * These are compressed event buffers that are used in case there are too many
+ * data pending to store them in the two uncompressed buffers.
+ **/
+
+/** Maximum number of compressed buffers supported. **/
+#define MAX_COMPRESSED_BUFFER_COUNT (64)
+static char *compressed_buffers[MAX_COMPRESSED_BUFFER_COUNT];
+static int compressed_buffer_size[MAX_COMPRESSED_BUFFER_COUNT];
+static int compressed_buffer_count = 0;
+
+
+/**
+ * Possible status values. RUNNING means the fschange module is active and
+ * processing events. TERMINATING means somebody called rmmod. All processes
+ * currently inside the module should leave it so that it may be unloaded from
+ * the kernel.
+ **/
+static const int RUNNING = 0;
+static const int TERMINATING = 1;
+static int fschange_status;
+
+
+/**
+ * Since we can have multiple parallel tasks, i.e. multiple processes
+ * opening/closing files and thus calling the notifier, we need to protect
+ * our data in some way.
+ **/
+static struct semaphore data_lock;
+
+/**
+ * This guy tells us when there is new data available than can be delivered
+ * to userspace. Used for blocking I/O on the /proc file.
+ **/
+static struct semaphore data_available;
+
+
+/** Returns the system time in milli-seconds. We can always use this. **/
+static inline unsigned long currentTimeMillis(void) {
+/*
+  struct timeval tv;
+  do_gettimeofday(&tv);
+  unsigned long result = (tv.tv_sec & 0x000FFFFF) * 1000;
+  result += tv.tv_usec / 1000;
+  return result;
+*/
+  return jiffies * 1000 / HZ;
+} // end of currentTimeMillis()
+
+
+static int print_offset(char *buffer, loff_t offset) {
+	unsigned int upper, lower;
+	if (offset < 0)
+		return sprintf(buffer, "0");
+	else if (offset >= 1000000000) {
+		upper = 0;
+		lower = 0;
+		while (offset >= 1000000000) {
+			offset -= 1000000000;
+			upper++;
+		}
+		lower = offset;
+		return sprintf(buffer, "%d%09d", upper, lower);
+	}
+	else {
+		lower = offset;
+		return sprintf(buffer, "%d", lower);
+	}
+} // end of print_offset(char*, loff_t)
+
+
+/**
+ * Takes a full path name and transforms it in to its canonical form by
+ * removing ".", "..", etc.
+ **/
+static void simplify_filename(char *filename) {
+	char *pos, *ptr;
+	while ((pos = strstr(filename, "/./")) != NULL) {
+		ptr = &pos[2];
+		while (*ptr != 0)
+			*(pos++) = *(ptr++);
+		*pos = *ptr;
+	}
+	while ((pos = strstr(filename, "//")) != NULL) {
+		ptr = &pos[1];
+		while (*ptr != 0)
+			*(pos++) = *(ptr++);
+		*pos = *ptr;
+	}
+	while ((pos = strstr(filename, "/../")) != NULL) {
+		ptr = &pos[3];
+		if (pos != filename) {
+			pos--;
+			while (*pos != '/')
+				pos--;
+		}
+		while (*ptr != 0)
+			*(pos++) = *(ptr++);
+		*pos = *ptr;
+	}
+} // end of simplify_filename(char*)
+
+
+/**
+ * Adds the given event to the given buffer. Returns the number of characters
+ * printed to the buffer.
+ **/
+static int add_event(char *buffer, const char *event_type,
+		const char *file1, const char *file2, loff_t start, loff_t end) {
+	int result = 0;
+	result += sprintf(&buffer[result], "%s", event_type);
+	if (file1 != NULL)
+		result += sprintf(&buffer[result], "\t%s", file1);
+	if (file2 != NULL)
+		result += sprintf(&buffer[result], "\t%s", file2);
+	if (start >= 0) {
+		buffer[result++] = '\t';
+		result += print_offset(&buffer[result], start);
+	}
+	if (end >= 0) {
+		buffer[result++] = '\t';
+		result += print_offset(&buffer[result], end);
+	}
+	buffer[result++] = '\n';
+	buffer[result] = 0;
+	return result;
+} // end of add_event(...)
+
+
+static int compress_write_buffer(void) {
+	if (compressed_buffer_count >= MAX_COMPRESSED_BUFFER_COUNT)
+		return 0;
+	else if ((compressed_buffer_count == 0) &&
+	    (read_buffer_size + write_buffer_size < UNCOMPRESSED_BUFFER_SIZE)) {
+		memcpy(&read_buffer[read_buffer_size], write_buffer, write_buffer_size);
+		read_buffer_size += write_buffer_size;
+		read_buffer[read_buffer_size] = 0;
+		write_buffer_size = 0;
+		write_buffer[0] = 0;
+		return 1;
+	}
+	else {
+		// Changed compression back to something less sophisticated; there was a bug
+		// somewhere, but I could not find it.
+		int i, len, pos, best_len, best_pos;
+		int buffer_size = 0;
+		for (i = 0; i < write_buffer_size; ) {
+			best_len = best_pos = 0;
+			for (pos = i - 1; (pos >= 0) && (pos >= i - 127); pos--) {
+				len = 0;
+				while ((len < 127) && (i + len < write_buffer_size) && (write_buffer[i + len] == write_buffer[pos + len]))
+					len++;
+				if (len > best_len) {
+					best_len = len;
+					best_pos = pos;
+					if (len > 30)
+						break;
+				}
+			}
+			if (best_len > 3) {
+				temp_buffer[buffer_size++] = 0;
+				temp_buffer[buffer_size++] = (char)(best_pos - i);
+				temp_buffer[buffer_size++] = (char)best_len;
+				i += best_len;
+			}
+			else {
+				if (write_buffer[i] == 0)
+					temp_buffer[buffer_size++] = 0;
+				temp_buffer[buffer_size++] = write_buffer[i++];
+			}
+		}
+
+		// copy data to new compressed buffer
+		compressed_buffers[compressed_buffer_count] = kmalloc(buffer_size, GFP_KERNEL);
+		if (compressed_buffers[compressed_buffer_count] == NULL)
+			return 0;
+		memcpy(compressed_buffers[compressed_buffer_count], temp_buffer, buffer_size);
+		compressed_buffer_size[compressed_buffer_count] = buffer_size;
+		compressed_buffer_count++;
+
+		// empty the write buffer and return
+		write_buffer_size = 0;
+		write_buffer[0] = 0;
+		return 1;
+	}
+} // end of compress_write_buffer()
+
+
+static int uncompress_read_buffer(void) {
+	if ((compressed_buffer_count == 0) && (write_buffer_size > 0) &&
+	    (read_buffer_size + write_buffer_size < UNCOMPRESSED_BUFFER_SIZE)) {
+		memcpy(&read_buffer[read_buffer_size], write_buffer, write_buffer_size);
+		read_buffer_size += write_buffer_size;
+		read_buffer[read_buffer_size] = 0;
+		write_buffer_size = 0;
+		write_buffer[0] = 0;
+		return 1;
+	}
+	else if (compressed_buffer_count > 0) {
+		// Changed compression back to something less sophisticated; there was a bug
+		// somewhere, but I could not find it.
+		int i, k;
+		int size = compressed_buffer_size[0];
+		read_buffer_pos = 0;
+		read_buffer_size = 0;
+		for (i = 0; i < size; i++) {
+			if (compressed_buffers[0][i] == 0) {
+				int rel_pos = compressed_buffers[0][++i];
+				if (rel_pos >= 0) {
+					for (k = 0; k <= rel_pos; k++)
+						read_buffer[read_buffer_size++] = 0;
+				}
+				else {
+					int cnt = compressed_buffers[0][++i];
+					for (k = 0; k < cnt; k++)
+						read_buffer[read_buffer_size + k] = read_buffer[read_buffer_size + k + rel_pos];
+					read_buffer_size += cnt;
+				}
+			}
+			else
+				read_buffer[read_buffer_size++] = compressed_buffers[0][i];
+		}
+		read_buffer[read_buffer_size] = 0;
+
+		// free memory occupied by first compressed buffer and update references
+		kfree(compressed_buffers[0]);
+		for (i = 1; i < compressed_buffer_count; i++) {
+			compressed_buffers[i - 1] = compressed_buffers[i];
+			compressed_buffer_size[i - 1] = compressed_buffer_size[i];
+		}
+		compressed_buffer_count--;
+		return 1;
+	}
+	else
+		return 0;
+} // end of uncompress_read_buffer()
+
+
+/**
+ * This function is called every time a file inside the filesystem is changed.
+ * "message_type" can be WRITE, TRUNCATE, MOUNT, UNMOUNT, ...
+ * "file1" is the file that was changed. In case of a move operation, "file1"
+ *    is the old path and "file2" is the new path.
+ * In case of a WRITE operation, "start" if the first byte that may have been
+ *    changed, and "end" is the first byte after the interval that has been
+ *    changed.
+ **/
+static void my_fschange_notifier(int message_type, const char *file1,
+		const char *file2, loff_t start, loff_t end) {
+	char filename1[256];
+	char filename2[256];
+	int space_needed, old_read_buffer_size;
+
+	// check whether the message type is in the right range
+	if ((message_type < MIN_MESSAGE_TYPE) || (message_type > MAX_MESSAGE_TYPE))
+		return;
+
+	// acquire semaphore to change internal data
+	down(&data_lock);
+
+	if (fschange_status != RUNNING) {
+		up(&data_lock);
+		return;
+	}
+
+	if (file1 != NULL) {
+		if (strlen(file1) < 255) {
+			strcpy(filename1, file1);
+			simplify_filename(filename1);
+			file1 = filename1;
+		}
+	}
+
+	if (file2 != NULL) {
+		if (strlen(file2) < 255) {
+			strcpy(filename2, file2);
+			simplify_filename(filename2);
+			file2 = filename2;
+		}
+	}
+
+	space_needed = 32 +
+		(file1 == NULL ? 0 : strlen(file1)) +
+		(file2 == NULL ? 0 : strlen(file2)) +
+		(start < 0 ? 0 : 16) + (end < 0 ? 0 : 16);
+
+	if (space_needed >= MAX_EVENT_LENGTH) {
+		// we refuse to process file system events that are too long
+		up(&data_lock);
+		return;
+	}
+
+	old_read_buffer_size = read_buffer_size;
+
+my_fschange_notifier_try_again:
+
+	if ((write_buffer_size > 0) || (compressed_buffer_count > 0)) {
+		// if there are data in the write buffer, we have to append this
+		// event to the write buffer
+		if (write_buffer_size + space_needed >= UNCOMPRESSED_BUFFER_SIZE - 32) {
+			if (!compress_write_buffer()) {
+				// unable to compress data into additional buffer: write error message
+				// and return to caller
+				if (write_buffer_size <= UNCOMPRESSED_BUFFER_SIZE - 32)
+					write_buffer_size +=
+						sprintf(&write_buffer[write_buffer_size], "ERROR\tbuffer full\n");
+				up(&data_lock);
+				return;
+			}
+			write_buffer_size = 0;
+			write_buffer[0] = 0;
+			goto my_fschange_notifier_try_again;
+		}
+		write_buffer_size += add_event(&write_buffer[write_buffer_size],
+				event_strings[message_type], file1, file2, start, end);
+	}
+	else if (read_buffer_size + space_needed < UNCOMPRESSED_BUFFER_SIZE) {
+		// check whether we can put the data directly into the read buffer
+		read_buffer_size += add_event(&read_buffer[read_buffer_size],
+				event_strings[message_type], file1, file2, start, end);
+	}
+	else if (read_buffer_pos > UNCOMPRESSED_BUFFER_SIZE / 4) {
+		// try to make more space in the read buffer
+		if (read_buffer_pos < read_buffer_size) {
+			read_buffer_size -= read_buffer_pos;
+			memmove(read_buffer, &read_buffer[read_buffer_pos], read_buffer_size);
+		}
+		else
+			read_buffer_size = 0;
+		read_buffer_pos = 0;
+		read_buffer_size += add_event(&read_buffer[read_buffer_size],
+				event_strings[message_type], file1, file2, start, end);
+	}
+	else {
+		// if nothing helps, we have to put the new event into the write buffer
+		// since, at this point, we know that write_buffer_size == 0, we can simply
+		// add it to the buffer without any further checks
+		write_buffer_size += add_event(&write_buffer[write_buffer_size],
+				event_strings[message_type], file1, file2, start, end);
+	}
+
+	// if before this operation there was no data available to be read, increase
+	// the "data_available" semaphore so that they can be read
+	if (old_read_buffer_size == 0)
+		up(&data_available);
+
+	up(&data_lock);
+} // end of my_fschange_notifier(char*, char*, char*)
+
+
+/**
+ * If somebody reads from /proc/changed_files, this function gets called.
+ * It returns the number of bytes read from the proc file.
+ **/
+static int read_proc_file(char *page, char **start, off_t off, int count, int *eof, void *data) {
+	static int reading = 0;
+	char *buffer = page;
+  
+	if ((fschange_status != RUNNING) || (off > 0)) {
+		*eof = 1;
+		return 0;
+	}
+
+	down(&data_lock);
+
+	if ((fschange_status != RUNNING) || (reading) || (read_buffer_size == 0)) {
+		// if the read buffer is empty or if there is already another process
+		// reading, return immediately
+		up(&data_lock);
+		*eof = reading;
+		return 0;
+	}
+
+	// set the "i am reading here; everybody else: get lost!" flag
+	reading = 1;
+
+	if (down_trylock(&data_available) != 0) {
+		// return value != 0 means no data available right now => wait a bit
+		up(&data_lock);
+		if (down_interruptible(&data_available) == -EINTR) {
+			// if we are interrupted, we return immediately
+			down(&data_lock);
+			reading = 0;
+			up(&data_lock);
+			*eof = 1;
+			return 0;
+		}
+		down(&data_lock);
+	}
+
+	// getting here means: data available;
+	// if in the meantime somebody unloaded the module, return immediately
+	if (fschange_status != RUNNING) {
+		up(&data_lock);
+		*eof = 1;
+		return 0;
+	}
+ 
+	// ignore the offset given by "off"
+
+	if (count >= read_buffer_size - read_buffer_pos) {
+		// check whether the whole read buffer can be read at once
+		count = read_buffer_size - read_buffer_pos;
+		memcpy(buffer, &read_buffer[read_buffer_pos], count);
+		read_buffer_size = read_buffer_pos = 0;
+		uncompress_read_buffer();
+	}
+	else {
+		// otherwise, only read that part of the read buffer that fits into
+		// the output buffer ("count" characters)
+		while ((count > MIN_READ_SIZE) && (read_buffer[read_buffer_pos + count - 1] != '\n'))
+			count--;
+		memcpy(buffer, &read_buffer[read_buffer_pos], count);
+		read_buffer_pos += count;
+		if (read_buffer_pos > UNCOMPRESSED_BUFFER_SIZE / 4) {
+			read_buffer_size -= read_buffer_pos;
+			if (read_buffer_size > 0)
+				memmove(read_buffer, &read_buffer[read_buffer_pos], read_buffer_size);
+			read_buffer_pos = 0;
+			uncompress_read_buffer();
+		}
+	} // end else [count < read_buffer_size - read_buffer_pos]
+
+	if (read_buffer_pos >= read_buffer_size) {
+		read_buffer_pos = read_buffer_size = 0;
+		uncompress_read_buffer();
+	}
+
+	*eof = 0;
+
+	// depending on whether there is more data to read, either increase the
+	// "data_available" semaphore or leave it where it is
+	if (read_buffer_size > read_buffer_pos)
+		up(&data_available);
+
+	// set the "a process is currently reading here" flag back to zero and
+	// release the lock
+	reading = 0;
+	up(&data_lock);
+
+	return count;
+} // end of read_proc_file(char*, char**, off_t, int, int*, void*)
+
+
+/**
+ * This function is called whenever somebody writes to our proc file.
+ * It returns the number of bytes written.
+ **/
+static int write_proc_file(struct file* file, const char* buffer, unsigned long count, void* data) {
+	count = 0;
+
+	if (fschange_status != RUNNING)
+		return 0;
+
+	down(&data_lock);
+	up(&data_lock);
+	return count;
+} // end of write_proc_file(struct file*, const char*, unsigned long, void*)
+
+
+static int __init fschange_init(void) {
+	int i;
+	fschange_status = RUNNING;
+
+	read_buffer = (char*)kmalloc(UNCOMPRESSED_BUFFER_SIZE, GFP_KERNEL);
+	read_buffer[0] = 0;
+	read_buffer_pos = 0;
+	read_buffer_size = 0;
+	write_buffer = (char*)kmalloc(UNCOMPRESSED_BUFFER_SIZE, GFP_KERNEL);
+	write_buffer_size = 0;
+	temp_buffer = (char*)kmalloc(UNCOMPRESSED_BUFFER_SIZE, GFP_KERNEL);
+	for (i = 0; i < MAX_COMPRESSED_BUFFER_COUNT; i++) {
+		compressed_buffers[i] = NULL;
+		compressed_buffer_size[i] = -1;
+	}
+
+	// create proc file
+	proc_file = create_proc_entry(PROCFILE_NAME, 0600, &proc_root);
+	if (proc_file != NULL) {
+		// set read and write function for proc file
+		proc_file->read_proc = read_proc_file;
+		proc_file->write_proc = write_proc_file;
+
+		// initialize semaphores
+		sema_init(&data_lock, 1);
+		sema_init(&data_available, 0);
+		down(&data_lock);
+		fschange_set_notifier(my_fschange_notifier);
+		up(&data_lock);
+
+		// print message to kernel message buffer and return
+		printk(KERN_INFO OK_MESSAGE);
+		return 0;
+	}
+	else {
+		if (read_buffer != NULL)
+			kfree(read_buffer);
+		if (write_buffer != NULL)
+			kfree(write_buffer);
+		if (temp_buffer != NULL)
+			kfree(temp_buffer);
+
+		// print message to kernel message buffer and return
+		printk(KERN_ERR ERROR_MESSAGE);
+		return 1;
+	}
+
+} // end of fschange_init
+
+
+static void __exit fschange_exit(void) {
+	int i;
+	static wait_queue_head_t module_exit_queue;
+
+	// stop the notifications
+	fschange_set_notifier(NULL);
+
+	// remove the proc file from the file system
+	remove_proc_entry(PROCFILE_NAME, &proc_root);
+
+	// set status to TERMINATING and increase the data_available semaphore;
+	// this guarantees that a potential reader will continue execution (and
+	// leave the read_proc_file function)
+	down(&data_lock);
+	fschange_status = TERMINATING;
+	up(&data_lock);
+	up(&data_available);
+
+	for (i = 0; i < 64; i++) {
+		up(&data_lock);
+		up(&data_available);
+	}
+
+	init_waitqueue_head(&module_exit_queue);
+	sleep_on_timeout(&module_exit_queue, HZ / 100 * 2);
+	sleep_on_timeout(&module_exit_queue, HZ / 100 * 2);
+
+	// free all allocated memory
+	kfree(read_buffer);
+	kfree(write_buffer);
+	kfree(temp_buffer);
+	for (i = 0; i < MAX_COMPRESSED_BUFFER_COUNT; i++)
+		if (compressed_buffers[i] != NULL)
+			kfree(compressed_buffers[i]);
+
+} // end of fschange_exit(void)
+                                                                                                        
+                                                                                                        
+module_init(fschange_init);
+module_exit(fschange_exit);
+
+
diff -Naur linux-2.6.16.13/fs/Kconfig linux-2.6.16.13-fschange/fs/Kconfig
--- linux-2.6.16.13/fs/Kconfig	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/fs/Kconfig	2006-05-02 23:52:29.000000000 -0400
@@ -449,6 +449,22 @@
 
 	  Because of this, if unsure, say Y.
 
+config FSCHANGE
+	tristate "fschange File System Notification"
+	default y
+	help
+	  fschange is a kernel interface (accessible through the /proc/fschange
+	  proc file) that can be used to monitor file system changes, such as file
+	  changes, directory deletions, and mounts/unmounts. It is not a real
+	  replacement for inotify because only root can read from the proc file.
+	  Read results based on user permissions might be added in the future.
+	  The main advantage of fschange over dnotify/inotify is that all changes
+	  to the whole file system may be read from the proc file -- without having
+	  to register for directory-specific notification. This way, no file system
+	  events are lost due to race conditions etc.
+
+	  See http://stefan.buettcher.org/cs/fschange/ for more information.
+
 config AUTOFS_FS
 	tristate "Kernel automounter support"
 	help
diff -Naur linux-2.6.16.13/fs/Makefile linux-2.6.16.13-fschange/fs/Makefile
--- linux-2.6.16.13/fs/Makefile	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/fs/Makefile	2006-05-02 23:55:07.000000000 -0400
@@ -10,7 +10,7 @@
 		ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \
 		attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \
 		seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \
-		ioprio.o pnode.o drop_caches.o
+		ioprio.o pnode.o drop_caches.o fschange.o
 
 obj-$(CONFIG_INOTIFY)		+= inotify.o
 obj-$(CONFIG_EPOLL)		+= eventpoll.o
@@ -41,6 +41,7 @@
 obj-$(CONFIG_QUOTACTL)		+= quota.o
 
 obj-$(CONFIG_DNOTIFY)		+= dnotify.o
+obj-$(CONFIG_FSCHANGE)		+= fschange_module.o
 
 obj-$(CONFIG_PROC_FS)		+= proc/
 obj-y				+= partitions/
diff -Naur linux-2.6.16.13/fs/namei.c linux-2.6.16.13-fschange/fs/namei.c
--- linux-2.6.16.13/fs/namei.c	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/fs/namei.c	2006-05-02 23:52:29.000000000 -0400
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/fschange.h>
 #include <linux/namei.h>
 #include <linux/quotaops.h>
 #include <linux/pagemap.h>
@@ -37,6 +38,7 @@
 
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
 
+
 /* [Feb-1997 T. Schoebel-Theuer]
  * Fundamental changes in the pathname lookup mechanisms (namei)
  * were necessary because of omirr.  The reason is that omirr needs
@@ -1576,6 +1578,7 @@
 	struct path path;
 	struct dentry *dir;
 	int count = 0;
+	int file_has_been_created = 0;
 
 	acc_mode = ACC_MODE(flag);
 
@@ -1639,6 +1642,7 @@
 		if (error)
 			goto exit;
 		/* Don't check for write permission, don't truncate */
+		file_has_been_created = 1;
 		acc_mode = 0;
 		flag &= ~O_TRUNC;
 		goto ok;
@@ -1672,6 +1676,13 @@
 	error = may_open(nd, acc_mode, flag);
 	if (error)
 		goto exit;
+
+	if (file_has_been_created) {
+		char *full_path = fschange_get_full_filename(pathname, current->fs);
+		fschange_notify(FSCHANGE_CREATE, full_path, NULL, -1, -1);
+		kfree(full_path);
+	}
+
 	return 0;
 
 exit_dput:
@@ -1894,6 +1905,12 @@
 			if (!IS_POSIXACL(nd.dentry->d_inode))
 				mode &= ~current->fs->umask;
 			error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
+			if (!error) {
+				// fschange notifier
+				char *full_path = fschange_get_full_filename(tmp, current->fs);
+				fschange_notify(FSCHANGE_MKDIR, full_path, NULL, -1, -1);
+				kfree(full_path);
+			}
 			dput(dentry);
 		}
 		mutex_unlock(&nd.dentry->d_inode->i_mutex);
@@ -2008,6 +2025,11 @@
 exit1:
 	path_release(&nd);
 exit:
+	if (!error) {
+		char *full_path = fschange_get_full_filename(name, current->fs);
+		fschange_notify(FSCHANGE_RMDIR, full_path, NULL, -1, -1);
+		kfree(full_path);
+	}
 	putname(name);
 	return error;
 }
@@ -2060,6 +2082,7 @@
 	struct dentry *dentry;
 	struct nameidata nd;
 	struct inode *inode = NULL;
+	char *tmp, *full_filename;
 
 	name = getname(pathname);
 	if(IS_ERR(name))
@@ -2088,6 +2111,16 @@
 	mutex_unlock(&nd.dentry->d_inode->i_mutex);
 	if (inode)
 		iput(inode);	/* truncate the inode here */
+
+	// fschange notifier
+	tmp = getname(pathname);
+	full_filename = fschange_get_full_filename(tmp, current->fs);
+	if ((error == 0) && (full_filename != NULL)) {
+		fschange_notify(FSCHANGE_UNLINK, full_filename, NULL, -1, -1);
+		kfree(full_filename);
+	}
+	putname(tmp);
+
 exit1:
 	path_release(&nd);
 exit:
@@ -2509,6 +2542,24 @@
 		error = do_rename(olddfd, from, newdfd, to);
 		putname(to);
 	}
+
+	if (!error) {
+		// fschange notifier
+		char *tmp_old = getname(oldname);
+		char *tmp_new = getname(newname);
+		char *full_filename_old = fschange_get_full_filename(tmp_old, current->fs);
+		char *full_filename_new = fschange_get_full_filename(tmp_new, current->fs);
+		if ((full_filename_old != NULL) && (full_filename_new != NULL)) {
+			fschange_notify(FSCHANGE_RENAME, full_filename_old, full_filename_new, -1, -1);
+		}
+		if (full_filename_new != NULL)
+			kfree(full_filename_new);
+		if (full_filename_old != NULL)
+			kfree(full_filename_old);
+		putname(tmp_new);
+		putname(tmp_old);
+	}
+
 	putname(from);
 	return error;
 }
diff -Naur linux-2.6.16.13/fs/namespace.c linux-2.6.16.13-fschange/fs/namespace.c
--- linux-2.6.16.13/fs/namespace.c	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/fs/namespace.c	2006-05-02 23:52:29.000000000 -0400
@@ -9,6 +9,7 @@
  */
 
 #include <linux/config.h>
+#include <linux/fschange.h>
 #include <linux/syscalls.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
@@ -27,6 +28,8 @@
 #include <asm/unistd.h>
 #include "pnode.h"
 
+int copy_mount_options(const void __user *data, unsigned long *where);
+
 extern int __init init_rootfs(void);
 
 #ifdef CONFIG_SYSFS
@@ -596,7 +599,12 @@
 asmlinkage long sys_umount(char __user * name, int flags)
 {
 	struct nameidata nd;
-	int retval;
+	int retval, cmo_status;
+	unsigned long page;
+
+	cmo_status = copy_mount_options(name, &page);
+	if (cmo_status >= 0)
+		fschange_notify(FSCHANGE_UMOUNT_REQ, (char*)page, NULL, -1, -1);
 
 	retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
 	if (retval)
@@ -612,9 +620,16 @@
 		goto dput_and_out;
 
 	retval = do_umount(nd.mnt, flags);
+
+	if ((retval >= 0) && (cmo_status >= 0))
+		fschange_notify(FSCHANGE_UMOUNT, (char*)page, NULL, -1, -1);
+
 dput_and_out:
 	path_release_on_umount(&nd);
 out:
+	if (cmo_status >= 0)
+		free_page(page);
+
 	return retval;
 }
 
@@ -1320,6 +1335,10 @@
 	else
 		retval = do_new_mount(&nd, type_page, flags, mnt_flags,
 				      dev_name, data_page);
+
+	if (retval >= 0)
+		fschange_notify(FSCHANGE_MOUNT, dev_name, dir_name, -1, -1);
+
 dput_out:
 	path_release(&nd);
 	return retval;
@@ -1467,6 +1486,7 @@
 	putname(dir_page);
 out1:
 	free_page(type_page);
+
 	return retval;
 }
 
diff -Naur linux-2.6.16.13/fs/open.c linux-2.6.16.13-fschange/fs/open.c
--- linux-2.6.16.13/fs/open.c	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/fs/open.c	2006-05-02 23:59:58.000000000 -0400
@@ -23,6 +23,7 @@
 #include <linux/fcntl.h>
 #include <asm/uaccess.h>
 #include <linux/fs.h>
+#include <linux/fschange.h>
 #include <linux/personality.h>
 #include <linux/pagemap.h>
 #include <linux/syscalls.h>
@@ -223,6 +224,7 @@
 {
 	struct nameidata nd;
 	struct inode * inode;
+	char *tmp, *full_filename;
 	int error;
 
 	error = -EINVAL;
@@ -273,6 +275,16 @@
 	}
 	put_write_access(inode);
 
+	// fschange notifier
+	tmp = getname(path);
+	full_filename = fschange_get_full_filename(tmp, current->fs);
+	if (full_filename != NULL) {
+		fschange_notify(FSCHANGE_TRUNCATE, full_filename, NULL, length, -1);
+		kfree(full_filename);
+	}
+	putname(tmp);
+	
+
 dput_and_out:
 	path_release(&nd);
 out:
@@ -287,6 +299,7 @@
 
 static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
 {
+
 	struct inode * inode;
 	struct dentry *dentry;
 	struct file * file;
@@ -320,8 +333,18 @@
 		goto out_putf;
 
 	error = locks_verify_truncate(inode, file, length);
-	if (!error)
+	if (!error) {
 		error = do_truncate(dentry, length, 0, file);
+		if (!error) {
+			// fschange notifier
+			char *full_filename;
+			full_filename = fschange_get_full_filename(file->f_name, current->fs);
+			if (full_filename != NULL) {
+				fschange_notify(FSCHANGE_TRUNCATE, full_filename, NULL, length, -1);
+				kfree(full_filename);
+			}
+		}
+	}
 out_putf:
 	fput(file);
 out:
@@ -624,6 +647,7 @@
 	struct file * file;
 	int err = -EBADF;
 	struct iattr newattrs;
+	char *full_filename;
 
 	file = fget(fd);
 	if (!file)
@@ -646,6 +670,13 @@
 	err = notify_change(dentry, &newattrs);
 	mutex_unlock(&inode->i_mutex);
 
+	// fschange notifier
+	full_filename = fschange_get_full_filename(file->f_name, current->fs);
+	if (full_filename != NULL) {
+		fschange_notify(FSCHANGE_CHMOD, full_filename, NULL, mode, -1);
+		kfree(full_filename);
+	}
+
 out_putf:
 	fput(file);
 out:
@@ -659,6 +690,7 @@
 	struct inode * inode;
 	int error;
 	struct iattr newattrs;
+	char *tmp, *full_filename;
 
 	error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
 	if (error)
@@ -681,6 +713,15 @@
 	error = notify_change(nd.dentry, &newattrs);
 	mutex_unlock(&inode->i_mutex);
 
+	// fschange notifier
+	tmp = getname(filename);
+	full_filename = fschange_get_full_filename(tmp, current->fs);
+	if (full_filename != NULL) {
+		fschange_notify(FSCHANGE_CHMOD, full_filename, NULL, mode, -1);
+		kfree(full_filename);
+	}
+	putname(tmp);
+
 dput_and_out:
 	path_release(&nd);
 out:
@@ -737,6 +778,30 @@
 		error = chown_common(nd.dentry, user, group);
 		path_release(&nd);
 	}
+
+	// fschange notifier
+	if (!error) {
+		char *tmp = getname(filename);
+		char *full_filename = fschange_get_full_filename(tmp, current->fs);
+		if (full_filename != NULL) {
+			fschange_notify(FSCHANGE_CHOWN, full_filename, NULL, user, group);
+			kfree(full_filename);
+		}
+		putname(tmp);
+	}
+
+
+	// fschange notifier
+	if (!error) {
+		char *tmp = getname(filename);
+		char *full_filename = fschange_get_full_filename(tmp, current->fs);
+		if (full_filename != NULL) {
+			fschange_notify(FSCHANGE_CHOWN, full_filename, NULL, user, group);
+			kfree(full_filename);
+		}
+		putname(tmp);
+	}
+
 	return error;
 }
 
@@ -782,6 +847,16 @@
 	file = fget(fd);
 	if (file) {
 		error = chown_common(file->f_dentry, user, group);
+
+		// fschange notifier
+		if (!error) {
+			char *full_filename = fschange_get_full_filename(file->f_name, current->fs);
+			if (full_filename != NULL) {
+				fschange_notify(FSCHANGE_CHOWN, full_filename, NULL, user, group);
+				kfree(full_filename);
+			}
+		}
+
 		fput(file);
 	}
 	return error;
@@ -809,6 +884,9 @@
 	f->f_vfsmnt = mnt;
 	f->f_pos = 0;
 	f->f_op = fops_get(inode->i_fop);
+	f->f_name = NULL;
+	f->f_start_of_change = 0;
+	f->f_end_of_change = 0;
 	file_move(f, &inode->i_sb->s_files);
 
 	if (!open && f->f_op)
@@ -874,8 +952,19 @@
 		namei_flags++;
 
 	error = open_namei(dfd, filename, namei_flags, mode, &nd);
-	if (!error)
-		return nameidata_to_filp(&nd, flags);
+	if (!error) {
+		struct file *result = nameidata_to_filp(&nd, flags);
+		if ((result != 0) && (!IS_ERR(result)) && (filename != 0) && (system_state == SYSTEM_RUNNING)) {
+			// fschange notifier
+			if (namei_flags & O_TRUNC)
+				fschange_notify(FSCHANGE_TRUNCATE, filename, NULL, 0, -1);
+			result->f_name = fschange_get_full_filename(filename, current->fs);
+			result->f_start_of_change = 0;
+			result->f_end_of_change = 0;
+			result->f_append_position = result->f_mapping->host->i_size;
+		}
+		return result;
+	}
 
 	return ERR_PTR(error);
 }
@@ -1142,6 +1231,19 @@
 		return 0;
 	}
 
+	// fschange notifier
+	if (file_count(filp) <= 1) {
+		// do only flush if all handles that refer to this file have been released
+		if (filp->f_name != NULL) {
+			if (filp->f_end_of_change > 0) {
+				// the file has been written to underway: tell the userland process
+				fschange_notify(FSCHANGE_WRITE, filp->f_name, NULL, filp->f_start_of_change, filp->f_end_of_change);
+			}
+			kfree(filp->f_name);
+			filp->f_name = NULL;
+		}
+	} // end if (file_count(filp) <= 1)
+
 	if (filp->f_op && filp->f_op->flush)
 		retval = filp->f_op->flush(filp);
 
diff -Naur linux-2.6.16.13/fs/read_write.c linux-2.6.16.13-fschange/fs/read_write.c
--- linux-2.6.16.13/fs/read_write.c	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/fs/read_write.c	2006-05-02 23:52:29.000000000 -0400
@@ -312,10 +312,26 @@
 		count = ret;
 		ret = security_file_permission (file, MAY_WRITE);
 		if (!ret) {
+			loff_t original_position = file->f_pos;
+
 			if (file->f_op->write)
 				ret = file->f_op->write(file, buf, count, pos);
 			else
 				ret = do_sync_write(file, buf, count, pos);
+
+			if (ret > 0) {
+				// fschange update
+				if (file->f_flags & O_APPEND)
+					original_position = file->f_append_position;
+				if ((original_position < file->f_start_of_change) ||
+				    (file->f_end_of_change == 0))
+					file->f_start_of_change = original_position;
+				if (original_position + ret > file->f_end_of_change)
+					file->f_end_of_change = original_position + ret;
+				if (file->f_flags & O_APPEND)
+					file->f_append_position += ret;
+			}
+
 			if (ret > 0) {
 				fsnotify_modify(file->f_dentry);
 				current->wchar += ret;
@@ -524,6 +540,10 @@
 	} else {
 		fn = (io_fn_t)file->f_op->write;
 		fnv = file->f_op->writev;
+
+		// fschange update
+		file->f_start_of_change = 0;
+		file->f_end_of_change = FSCHANGE_MAX_END_OF_CHANGE;
 	}
 	if (fnv) {
 		ret = fnv(file, iov, nr_segs, pos);
@@ -641,7 +661,7 @@
 {
 	struct file * in_file, * out_file;
 	struct inode * in_inode, * out_inode;
-	loff_t pos;
+	loff_t pos, original_position;
 	ssize_t retval;
 	int fput_needed_in, fput_needed_out;
 
@@ -682,6 +702,7 @@
 	out_file = fget_light(out_fd, &fput_needed_out);
 	if (!out_file)
 		goto fput_in;
+
 	if (!(out_file->f_mode & FMODE_WRITE))
 		goto fput_out;
 	retval = -EINVAL;
@@ -711,9 +732,24 @@
 		count = max - pos;
 	}
 
+	original_position = out_file->f_pos;
+
 	retval = in_file->f_op->sendfile(in_file, ppos, count, file_send_actor, out_file);
 
 	if (retval > 0) {
+		// fschange update
+		if (out_file->f_flags & O_APPEND)
+			original_position = out_file->f_append_position;
+		if ((original_position < out_file->f_start_of_change) ||
+		    (out_file->f_end_of_change == 0))
+			out_file->f_start_of_change = original_position;
+		if (original_position + count > out_file->f_end_of_change)
+			out_file->f_end_of_change = original_position + retval;
+		if (out_file->f_flags & O_APPEND)
+			out_file->f_append_position += retval;
+	}
+
+	if (retval > 0) {
 		current->rchar += retval;
 		current->wchar += retval;
 	}
diff -Naur linux-2.6.16.13/include/linux/fschange.h linux-2.6.16.13-fschange/include/linux/fschange.h
--- linux-2.6.16.13/include/linux/fschange.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.16.13-fschange/include/linux/fschange.h	2006-05-02 23:52:29.000000000 -0400
@@ -0,0 +1,44 @@
+/**
+ * This header file contains all necessary definitions for the fschange
+ * kernel patch.
+ *
+ * Copyright (C) 2005 Stefan Buettcher
+ **/
+
+
+#ifndef __FSCHANGE__H
+#define __FSCHANGE__H
+
+
+#include <linux/file.h>
+#include <linux/vfs.h>
+#include <linux/fs_struct.h>
+
+
+#define FSCHANGE_MOUNT      0x01
+#define FSCHANGE_UMOUNT     0x02
+#define FSCHANGE_UMOUNT_REQ 0x03
+#define FSCHANGE_WRITE      0x04
+#define FSCHANGE_TRUNCATE   0x05
+#define FSCHANGE_RENAME     0x06
+#define FSCHANGE_UNLINK     0x07
+#define FSCHANGE_CHMOD      0x08
+#define FSCHANGE_CHOWN      0x09
+#define FSCHANGE_CREATE     0x0A
+#define FSCHANGE_MKDIR      0x0B
+#define FSCHANGE_RMDIR      0x0C
+
+
+char *fschange_get_full_filename(const char *rel_filename, struct fs_struct *fs);
+
+extern void (*fschange_notifier)(int, const char*, const char*, loff_t, loff_t);
+
+void fschange_set_notifier(void (*new_notifier)(int, const char*, const char*, loff_t, loff_t));
+
+void fschange_notify(int messageType, const char *oldFile, const char *newFile,
+		loff_t from, loff_t to);
+
+
+#endif
+
+
diff -Naur linux-2.6.16.13/include/linux/fs.h linux-2.6.16.13-fschange/include/linux/fs.h
--- linux-2.6.16.13/include/linux/fs.h	2006-05-02 17:38:44.000000000 -0400
+++ linux-2.6.16.13-fschange/include/linux/fs.h	2006-05-02 23:52:29.000000000 -0400
@@ -646,7 +646,21 @@
 	spinlock_t		f_ep_lock;
 #endif /* #ifdef CONFIG_EPOLL */
 	struct address_space	*f_mapping;
+	/* The following member was added so that we can keep track of open files by their names. */
+	char *f_name;
+	/* We have to keep track of file changes: start of change and end of change (cumulative). */
+	loff_t f_start_of_change;
+	/* First unchanged byte after interval with changed content. */
+	loff_t f_end_of_change;
+	/*
+	 * O_APPEND is a pain in the ass, since the file pointer may go crazy. We use this guy
+	 * here to keep track of the current file pointer position in case of O_APPEND.
+	 */
+	loff_t f_append_position;
 };
+
+#define FSCHANGE_MAX_END_OF_CHANGE 2147483640
+
 extern spinlock_t files_lock;
 #define file_list_lock() spin_lock(&files_lock);
 #define file_list_unlock() spin_unlock(&files_lock);
