diff -Naur linux-2.6.12/arch/i386/kernel/sys_i386.c linux-2.6.12-fschange/arch/i386/kernel/sys_i386.c
--- linux-2.6.12/arch/i386/kernel/sys_i386.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/arch/i386/kernel/sys_i386.c	2005-06-27 16:46:41.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.12/arch/ia64/kernel/sys_ia64.c linux-2.6.12-fschange/arch/ia64/kernel/sys_ia64.c
--- linux-2.6.12/arch/ia64/kernel/sys_ia64.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/arch/ia64/kernel/sys_ia64.c	2005-06-27 16:46:41.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.12/arch/x86_64/kernel/sys_x86_64.c linux-2.6.12-fschange/arch/x86_64/kernel/sys_x86_64.c
--- linux-2.6.12/arch/x86_64/kernel/sys_x86_64.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/arch/x86_64/kernel/sys_x86_64.c	2005-06-27 16:46:41.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.12/fs/fcntl.c linux-2.6.12-fschange/fs/fcntl.c
--- linux-2.6.12/fs/fcntl.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/fs/fcntl.c	2005-06-27 16:46:41.396257376 -0400
@@ -121,8 +121,8 @@
 asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
 {
 	int err = -EBADF;
-	struct file * file, *tofree;
-	struct files_struct * files = current->files;
+	struct file *file, *tofree;
+	struct files_struct *files = current->files;
 
 	spin_lock(&files->file_lock);
 	if (!(file = fcheck(oldfd)))
@@ -153,12 +153,14 @@
 		goto out_fput;
 
 	files->fd[newfd] = file;
+
 	FD_SET(newfd, files->open_fds);
 	FD_CLR(newfd, files->close_on_exec);
 	spin_unlock(&files->file_lock);
 
 	if (tofree)
 		filp_close(tofree, files);
+
 	err = newfd;
 out:
 	return err;
diff -Naur linux-2.6.12/fs/file_table.c linux-2.6.12-fschange/fs/file_table.c
--- linux-2.6.12/fs/file_table.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/fs/file_table.c	2005-06-27 16:46:41.473245672 -0400
@@ -83,6 +83,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_list);
 			f->f_maxcount = INT_MAX;
diff -Naur linux-2.6.12/fs/fschange.c linux-2.6.12-fschange/fs/fschange.c
--- linux-2.6.12/fs/fschange.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-fschange/fs/fschange.c	2005-06-27 16:46:41.474245520 -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.12/fs/fschange_module.c linux-2.6.12-fschange/fs/fschange_module.c
--- linux-2.6.12/fs/fschange_module.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-fschange/fs/fschange_module.c	2005-06-27 16:46:41.552233664 -0400
@@ -0,0 +1,402 @@
+/*****
+ * 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: 2005-04-14
+ *
+ * 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>
+
+
+#define EXPORT_SYMTAB
+
+
+MODULE_AUTHOR("Stefan Buettcher <sbuettch@uwaterloo.ca>");
+MODULE_DESCRIPTION("File change notifier for the UW indexing service");
+MODULE_LICENSE("GPL");
+MODULE_INFO(vermagic, VERMAGIC_STRING); 
+
+
+#define PROCFILE_NAME "fschange"
+
+#define WELCOME_MESSAGE "-= fschange kernel module for linux 2.6.* =-\n"
+#define OKAY_MESSAGE    "[ module successfully loaded into kernel ]\n"
+#define ERROR_MESSAGE   "[ module could not be loaded -- procfile creation failed ]\n"
+
+#define WRITE_TTY(tty) tty->driver->write
+
+#define MIN(a, b) (a < b ? a : b)
+#define MAX(a, b) (a > b ? a : b)
+
+
+static const char *messageStrings[16] = {
+	NULL,
+	"MOUNT", "UMOUNT", "UMOUNT_REQ", "WRITE", "TRUNCATE", "RENAME", "UNLINK",
+	"CHMOD", "CHOWN", "CREATE", "MKDIR", "RMDIR",
+	NULL
+};
+
+
+/**
+ * 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* procFile;
+
+/** Handling read requests to the proc file. **/
+static int readProcFile(char* page, char** start, off_t off, int count, int* eof, void* data);
+
+/** Handling write requests to the proc file. **/
+static int writeProcFile(struct file* file, const char* buffer, unsigned long count, void* data);
+
+/** Where do we store the filenames until the next readProcFile? **/
+static char *filenameBuffer;
+
+/** Size of the buffer (in bytes). **/
+#define BUFFER_SIZE 65536
+
+static int bufferSize;
+
+
+#define RUNNING 0
+#define PLEASE_FINISH 1
+
+
+/** Set to PLEASE_FINISH is rmmod wants to remove us. **/
+static char terminate;
+
+/**
+ * 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 dataLock;
+
+/**
+ * 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 dataAvailable;
+
+
+/** 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*)
+
+
+/**
+ * This function is called every time a file inside the filesystem is changed.
+ * "messageType" 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.
+  changed.
+ **/
+static void my_fschange_notifier(int messageType, const char *file1,
+		const char *file2, loff_t start, loff_t end) {
+	char filename1[256];
+	char filename2[256];
+	int spaceNeeded, oldBufferSize;
+
+	if ((messageType <= 0) || (messageType >= 12))
+		return;
+	down(&dataLock);
+
+	oldBufferSize = bufferSize;
+
+	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;
+		}
+	}
+
+	spaceNeeded = 16 +
+		(file1 == NULL ? 0 : strlen(file1)) +
+		(file2 == NULL ? 0 : strlen(file2)) +
+		(start < 0 ? 0 : 16) + (end < 0 ? 0 : 16);
+
+	if (bufferSize + spaceNeeded >= BUFFER_SIZE) {
+		up(&dataLock);
+		return;
+	}
+
+	bufferSize += sprintf(&filenameBuffer[bufferSize], "%s", messageStrings[messageType]);
+	if (file1 != NULL)
+		bufferSize += sprintf(&filenameBuffer[bufferSize], "\t%s", file1);
+	if (file2 != NULL)
+		bufferSize += sprintf(&filenameBuffer[bufferSize], "\t%s", file2);
+	if (start >= 0) {
+		bufferSize += sprintf(&filenameBuffer[bufferSize], "\t");
+		bufferSize += print_offset(&filenameBuffer[bufferSize], start);
+	}
+	if (end >= 0) {
+		bufferSize += sprintf(&filenameBuffer[bufferSize], "\t");
+		bufferSize += print_offset(&filenameBuffer[bufferSize], end);
+	}
+	bufferSize += sprintf(&filenameBuffer[bufferSize], "\n");
+
+	// if before this operation there was no data available to be read, increase
+	// the "dataAvailable" semaphore so that they can be read
+	if (oldBufferSize == 0)
+		up(&dataAvailable);
+
+	up(&dataLock);
+} // 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 readProcFile(char *page, char **start, off_t off, int count, int *eof, void *data) {
+	static int reading = 0;
+	char *buffer = page;
+  
+	if ((terminate != RUNNING) || (off > 0)) {
+		*eof = 1;
+		return 0;
+	}
+
+	down(&dataLock);
+
+	if (reading) {
+		// if there is already a different process reading, return immediately
+		up(&dataLock);
+		*eof = 1;
+		return 0;
+	}
+
+	reading = 1;
+	if (count > 4096)
+		count = 4096;
+
+	if (down_trylock(&dataAvailable) != 0) {
+		// return value != 0 means no data available right now => wait a bit
+		up(&dataLock);
+		if (down_interruptible(&dataAvailable) == -EINTR) {
+			// if we are interrupted, we return immediately
+			down(&dataLock);
+			reading = 0;
+			up(&dataLock);
+			*eof = 1;
+			return 0;
+		}
+		down(&dataLock);
+	}
+
+	// getting here means: data available;
+	// if in the meantime somebody unloaded the module, return immediately
+	if (terminate != RUNNING) {
+		up(&dataLock);
+		*eof = 1;
+		return 0;
+	}
+ 
+	// ignore the offset given by "off"
+
+	// depending on the size of the read buffer ("count"), do different things	
+	if (count >= bufferSize) {
+		memcpy(buffer, filenameBuffer, bufferSize);
+		count = bufferSize;
+	}
+	else if (count > 0) {
+		while ((count > 32) && (filenameBuffer[count - 1] != '\n'))
+			count--;
+		memcpy(buffer, filenameBuffer, count);
+		memmove(filenameBuffer, &filenameBuffer[count], bufferSize - count);
+	}
+	*eof = 0;
+	if (count > 0)
+		bufferSize -= count;
+
+	// depending on whether there is more data to read, either increase the
+	// "dataAvailable" semaphore or leave it where it is
+	if (bufferSize > 0)
+		up(&dataAvailable);
+
+	// set the "a process is currently reading here" flax back to zero and
+	// release the lock
+	reading = 0;
+	up(&dataLock);
+
+	return count;
+} // end of readProcFile(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 writeProcFile(struct file* file, const char* buffer, unsigned long count, void* data) {
+	count = 0;
+
+	if (terminate != RUNNING)
+		return 0;
+
+	down(&dataLock);
+	up(&dataLock);
+	return count;
+} // end of writeProcFile(struct file*, const char*, unsigned long, void*)
+
+
+/** Prints the string given by "s" to the current task's tty. **/
+static void println(char* s) {
+//	struct tty_struct* targetTTY = current->tty;  // get the current task's tty
+//	if (targetTTY != NULL) {
+//		(WRITE_TTY(targetTTY))(targetTTY, 0, s, strlen(s));
+//		(WRITE_TTY(targetTTY))(targetTTY, 0, "\015\015", 2);
+//	}
+} // end of println(char*)
+
+
+static int __init fschange_init(void) {
+	int status;
+	terminate = RUNNING;
+	bufferSize = 0;
+	filenameBuffer = (char*)kmalloc(BUFFER_SIZE, GFP_KERNEL);
+	procFile = create_proc_entry(PROCFILE_NAME, 0600, &proc_root);
+	if (procFile != NULL) {
+		procFile->read_proc = readProcFile;
+		procFile->write_proc = writeProcFile;
+		// initialize mutex
+		sema_init(&dataLock, 1);
+		sema_init(&dataAvailable, 0);
+		// print a warm welcome message
+		println(WELCOME_MESSAGE);
+		println(OKAY_MESSAGE);
+		down(&dataLock);
+		fschange_set_notifier(my_fschange_notifier);
+		up(&dataLock);
+		status = 0;
+	}
+	else {
+		kfree(filenameBuffer);
+		println(WELCOME_MESSAGE);
+		println(ERROR_MESSAGE);
+		status = 1;
+	}
+	return status;
+} // end of fschange_init
+
+
+static void __exit fschange_exit(void) {
+	int i;
+	static wait_queue_head_t module_exit_queue;
+
+	fschange_set_notifier(NULL);
+
+	down(&dataLock);
+	terminate = PLEASE_FINISH;
+	up(&dataLock);
+	up(&dataAvailable);
+	
+	for (i = 0; i < 64; i++) {
+		up(&dataLock);
+		up(&dataAvailable);
+	}
+
+	// make sure everybody has finished execution before we actually exit
+	init_waitqueue_head(&module_exit_queue);
+	sleep_on_timeout(&module_exit_queue, HZ / 100 * 2);
+	remove_proc_entry(PROCFILE_NAME, &proc_root);
+	sleep_on_timeout(&module_exit_queue, HZ / 100 * 2);
+	kfree(filenameBuffer);
+} // end of fschange_exit(void)
+                                                                                                        
+                                                                                                        
+module_init(fschange_init);
+module_exit(fschange_exit);
+
+
diff -Naur linux-2.6.12/fs/Kconfig linux-2.6.12-fschange/fs/Kconfig
--- linux-2.6.12/fs/Kconfig	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/fs/Kconfig	2005-06-27 16:46:41.616223936 -0400
@@ -386,6 +386,22 @@
 
 	  Because of this, if unsure, say Y.
 
+config FSCHANGE
+	tristate "fschange File System Notification"
+	default m
+	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 dnotify yet 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 (and 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.
+
+	  See http://stefan.buettcher.org/cs/fschange/ for more information.
+
 config AUTOFS_FS
 	tristate "Kernel automounter support"
 	help
diff -Naur linux-2.6.12/fs/Makefile linux-2.6.12-fschange/fs/Makefile
--- linux-2.6.12/fs/Makefile	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/fs/Makefile	2005-06-27 16:46:41.691212536 -0400
@@ -10,6 +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 \
+		fschange.o
 
 obj-$(CONFIG_EPOLL)		+= eventpoll.o
 obj-$(CONFIG_COMPAT)		+= compat.o
@@ -38,6 +39,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.12/fs/namei.c linux-2.6.12-fschange/fs/namei.c
--- linux-2.6.12/fs/namei.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/fs/namei.c	2005-06-27 16:53:23.254165688 -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>
@@ -33,6 +34,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
@@ -1417,6 +1419,7 @@
 	struct path path;
 	struct dentry *dir;
 	int count = 0;
+	int file_has_been_created = 0;
 
 	acc_mode = ACC_MODE(flag);
 
@@ -1479,6 +1482,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;
@@ -1516,6 +1520,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:
@@ -1725,6 +1736,11 @@
 		up(&nd.dentry->d_inode->i_sem);
 		path_release(&nd);
 out:
+		if (!error) {
+			char *full_path = fschange_get_full_filename(tmp, current->fs);
+			fschange_notify(FSCHANGE_MKDIR, full_path, NULL, -1, -1);
+			kfree(full_path);
+		}
 		putname(tmp);
 	}
 
@@ -1830,6 +1846,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;
 }
@@ -1877,6 +1898,7 @@
 	struct dentry *dentry;
 	struct nameidata nd;
 	struct inode *inode = NULL;
+	char *tmp, *full_filename;
 
 	name = getname(pathname);
 	if(IS_ERR(name))
@@ -1905,6 +1927,16 @@
 	up(&nd.dentry->d_inode->i_sem);
 	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:
@@ -2298,6 +2330,24 @@
 		error = do_rename(from,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.12/fs/namespace.c linux-2.6.12-fschange/fs/namespace.c
--- linux-2.6.12/fs/namespace.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/fs/namespace.c	2005-06-27 16:46:42.128146112 -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>
@@ -25,6 +26,8 @@
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 
+int copy_mount_options(const void __user *data, unsigned long *where);
+
 extern int __init init_rootfs(void);
 
 #ifdef CONFIG_SYSFS
@@ -468,7 +471,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)
@@ -484,9 +492,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;
 }
 
@@ -1054,6 +1069,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;
@@ -1184,6 +1203,7 @@
 	putname(dir_page);
 out1:
 	free_page(type_page);
+
 	return retval;
 }
 
diff -Naur linux-2.6.12/fs/open.c linux-2.6.12-fschange/fs/open.c
--- linux-2.6.12/fs/open.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/fs/open.c	2005-06-27 16:46:42.200135168 -0400
@@ -17,15 +17,18 @@
 #include <linux/namei.h>
 #include <linux/backing-dev.h>
 #include <linux/security.h>
+#include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/vfs.h>
 #include <asm/uaccess.h>
 #include <linux/fs.h>
+#include <linux/fschange.h>
 #include <linux/pagemap.h>
 #include <linux/syscalls.h>
 
 #include <asm/unistd.h>
 
+
 int vfs_statfs(struct super_block *sb, struct kstatfs *buf)
 {
 	int retval = -ENODEV;
@@ -214,6 +217,7 @@
 {
 	struct nameidata nd;
 	struct inode * inode;
+	char *tmp, *full_filename;
 	int error;
 
 	error = -EINVAL;
@@ -264,6 +268,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:
@@ -278,6 +292,7 @@
 
 static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
 {
+
 	struct inode * inode;
 	struct dentry *dentry;
 	struct file * file;
@@ -311,8 +326,20 @@
 		goto out_putf;
 
 	error = locks_verify_truncate(inode, file, length);
-	if (!error)
+
+	if (!error) {
+		char *full_filename;
 		error = do_truncate(dentry, length);
+
+		// fschange notifier
+		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:
@@ -599,6 +626,7 @@
 	struct file * file;
 	int err = -EBADF;
 	struct iattr newattrs;
+	char *full_filename;
 
 	file = fget(fd);
 	if (!file)
@@ -621,6 +649,13 @@
 	err = notify_change(dentry, &newattrs);
 	up(&inode->i_sem);
 
+	// 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:
@@ -633,6 +668,7 @@
 	struct inode * inode;
 	int error;
 	struct iattr newattrs;
+	char *tmp, *full_filename;
 
 	error = user_path_walk(filename, &nd);
 	if (error)
@@ -655,6 +691,15 @@
 	error = notify_change(nd.dentry, &newattrs);
 	up(&inode->i_sem);
 
+	// 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:
@@ -706,6 +751,18 @@
 		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);
+	}
+
 	return error;
 }
 
@@ -719,6 +776,18 @@
 		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);
+	}
+
 	return error;
 }
 
@@ -731,11 +800,22 @@
 	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;
 }
 
+
 /*
  * Note that while the flag value (low two bits) for sys_open means:
  *	00 - read-only
@@ -762,8 +842,19 @@
 		namei_flags |= 2;
 
 	error = open_namei(filename, namei_flags, mode, &nd);
-	if (!error)
-		return dentry_open(nd.dentry, nd.mnt, flags);
+	if (!error) {
+		struct file * result = dentry_open(nd.dentry, nd.mnt, flags);
+		if ((result != NULL) && (!IS_ERR(result)) && (filename != NULL) &&
+		    (system_state == SYSTEM_RUNNING)) {
+			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);
 }
@@ -794,6 +885,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 (f->f_op && f->f_op->open) {
@@ -982,6 +1076,19 @@
 {
 	int retval;
 
+	// 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 (filp->f_name != NULL)
+	}
+
 	/* Report and clear outstanding errors */
 	retval = filp->f_error;
 	if (retval)
diff -Naur linux-2.6.12/fs/read_write.c linux-2.6.12-fschange/fs/read_write.c
--- linux-2.6.12/fs/read_write.c	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/fs/read_write.c	2005-06-27 16:46:42.202134864 -0400
@@ -282,10 +282,26 @@
 	if (!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) {
 				dnotify_parent(file->f_dentry, DN_MODIFY);
 				current->wchar += ret;
@@ -491,6 +507,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);
@@ -605,7 +625,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;
 
@@ -645,6 +665,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;
@@ -673,9 +694,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.12/include/linux/fschange.h linux-2.6.12-fschange/include/linux/fschange.h
--- linux-2.6.12/include/linux/fschange.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-fschange/include/linux/fschange.h	2005-06-27 16:46:42.271124376 -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.12/include/linux/fs.h linux-2.6.12-fschange/include/linux/fs.h
--- linux-2.6.12/include/linux/fs.h	2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6.12-fschange/include/linux/fs.h	2005-06-27 16:46:42.274123920 -0400
@@ -600,7 +600,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);
