[linux-cifs-client,1/2] cifs_lock_storage module
diff mbox

Message ID 200903162112.36647.piastry@etersoft.ru
State New, archived
Headers show

Commit Message

Pavel Shilovsky March 16, 2009, 6:12 p.m. UTC
--
Best regards,
Pavel Shilovsky.

Patch
diff mbox

diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index 6ba43fb..3a01491 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -6,7 +6,7 @@  obj-$(CONFIG_CIFS) += cifs.o
 cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
 	  link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o \
 	  md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o \
-	  readdir.o ioctl.o sess.o export.o cifsacl.o
+	  readdir.o ioctl.o sess.o export.o cifsacl.o cifs_lock_storage.o
 
 cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 
diff --git a/fs/cifs/cifs_lock_storage.c b/fs/cifs/cifs_lock_storage.c
new file mode 100644
index 0000000..1e9371c
--- /dev/null
+++ b/fs/cifs/cifs_lock_storage.c
@@ -0,0 +1,228 @@ 
+/*
+ *   fs/cifs/cifs_lock_storage.c
+ *
+ *   lock tree operations that provide posix behaviour on windows server
+ *
+ *   Author(s): Pavel Shilovsky (piastryyy@gmail.com), Copyright (C) 2009.
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/ctype.h>
+#include <linux/kthread.h>
+#include <linux/random.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_lock_storage.h"
+
+struct cifsPidTreeLock {
+	struct list_head lock_list;
+	__u8 type;
+	__u64 offset;
+	__u64 len;
+	__u16 fid;
+	unsigned long inodeId;
+};
+
+struct cifsPidTree {
+	struct cifsPidTree *left, *right;
+	__u64 priority;
+	__u32 pid;
+	struct list_head lock_list;
+};
+
+/* Head of lock tree */
+static struct cifsPidTree *LOCK_STORAGE;
+static struct mutex storage_mutex;
+
+void cifs_lock_storage_init(void)
+{
+	mutex_init(&storage_mutex);
+	LOCK_STORAGE = NULL;
+}
+
+/* Create vertex of lock tree */
+static int vertex_init(struct cifsPidTree **root, __u32 pid)
+{
+	(*root) = kmalloc(sizeof(struct cifsPidTree), GFP_KERNEL);
+	if ((*root) == NULL) {
+		return -ENOMEM;
+	}
+	(*root)->pid = pid;
+	(*root)->left = (*root)->right = NULL;
+	(*root)->priority = ((random32() << 16) ^ random32());
+	INIT_LIST_HEAD(&(*root)->lock_list);
+	return 0;
+}
+
+/* Split - base operation of Descartes' tree. Split tree throw element pid.
+	As a result we have two tree - one with elements less than pid,
+	another - with more or equal */
+static void cifs_pid_tree_split(
+		struct cifsPidTree *root, struct cifsPidTree **left,
+		struct cifsPidTree **right, __u32 pid)
+{
+	if (!root) {
+		(*left) = (*right) = NULL;
+		return;
+	}
+
+	if (pid <= root->pid) {
+		(*right) = root;
+		cifs_pid_tree_split(root->left, left, &((*right)->left), pid);
+	} else {
+		(*left) = root;
+		cifs_pid_tree_split(root->right, &((*left)->right), right, pid);
+	}
+}
+
+/* Merge - base operation of Descartes' tree. Megre two trees into one.
+	Left consists of elements less than pid,
+	right - more or equal. */
+static struct cifsPidTree * cifs_pid_tree_merge(
+		struct cifsPidTree *left, struct cifsPidTree *right)
+{
+	struct cifsPidTree *result = NULL;
+	if (!left) {
+		return right;
+	}
+	if (!right) {
+		return left;
+	}
+	if (left->priority <= right->priority) {
+		result = right;
+		result->left = cifs_pid_tree_merge(left, right->left);
+	} else {
+		result = left;
+		result->right = cifs_pid_tree_merge(left->right, right);
+	}
+	return result;
+}
+
+/* Find element with pid in tree */
+static struct cifsPidTree ** cifs_pid_tree_find_pid(
+		struct cifsPidTree **root, __u32 pid)
+{
+	if (!(*root)) {
+		return root;
+	}
+	if (pid == (*root)->pid) {
+		return root;
+	} else if (pid < (*root)->pid) {
+		return cifs_pid_tree_find_pid(&((*root)->left), pid);
+	} else {
+		return cifs_pid_tree_find_pid(&((*root)->right), pid);
+	}
+}
+
+/* Add element into lock tree */
+static int __cifs_lock_storage_add_lock(
+		struct cifsPidTree **root, __u32 pid,
+		unsigned long inodeId, __u16 fid, __u64 offset,
+		__u64 len, __u8 type)
+{
+	struct cifsPidTreeLock *new_lock = NULL;
+	int rc = 0;
+	struct cifsPidTree **exist = NULL;
+	exist = cifs_pid_tree_find_pid(root, pid);
+	new_lock = kmalloc(sizeof(struct cifsPidTreeLock), GFP_KERNEL);
+	if (new_lock == NULL) {
+		return -ENOMEM;
+	}
+	new_lock->offset = offset;
+	new_lock->len = len;
+	new_lock->type = type;
+	new_lock->fid = fid;
+	new_lock->inodeId = inodeId;
+	if (!(*exist)) {
+		struct cifsPidTree *left = NULL, *right = NULL, *new_item = NULL;
+		rc = vertex_init(&new_item, pid);
+		if (rc) {
+			kfree(&new_lock);
+			return rc;
+		}
+		list_add(&new_lock->lock_list, &new_item->lock_list);
+		cifs_pid_tree_split((*root), &left, &right, pid);
+		(*root) = cifs_pid_tree_merge(left, new_item);
+		(*root) = cifs_pid_tree_merge((*root), right);
+	} else {
+		list_add(&new_lock->lock_list, &(*exist)->lock_list);
+	}
+	return rc;
+}
+
+/* Delete element from lock tree */
+static int __cifs_lock_storage_del_lock(
+		int xid, struct cifsTconInfo *pTcon, struct cifsPidTree **root,
+		__u32 pid, unsigned long inodeId, __u64 offset, __u64 len, __u8 type)
+{
+	struct cifsPidTree **exist;
+	int rc = 0;
+	struct cifsPidTreeLock *li, *tmp;
+	exist = cifs_pid_tree_find_pid(root, pid);
+	if (!(*exist)) {
+		return -1;
+	}
+	list_for_each_entry_safe(li, tmp, &(*exist)->lock_list, lock_list) {
+		if (li->offset >= offset && (li->offset+li->len <= offset+len)
+										&& (inodeId == li->inodeId)) {
+			int tmp_rc = CIFSSMBLock(xid, pTcon,
+				li->fid,
+				li->len, li->offset,
+				1, 0, li->type, false);
+			if (tmp_rc) {
+				rc = tmp_rc;
+			}
+			list_del(&li->lock_list);
+			kfree(li);
+		}
+	}
+	if (list_empty(&((*exist)->lock_list))) {
+		struct cifsPidTree *temp;
+		temp = (*exist);
+		(*exist) = cifs_pid_tree_merge((*exist)->left, (*exist)->right);
+		kfree(temp);
+	}
+	return rc;
+}
+
+int cifs_lock_storage_add_lock(
+		__u32 pid, unsigned long inodeId, __u16 fid,
+		__u64 offset, __u64 len, __u8 type)
+{
+	int rc;
+	mutex_lock(&storage_mutex);
+	rc = __cifs_lock_storage_add_lock(&LOCK_STORAGE, pid,
+					inodeId, fid, offset, len, type);
+	mutex_unlock(&storage_mutex);
+	return rc;
+}
+
+int cifs_lock_storage_del_lock(
+		int xid, struct cifsTconInfo *pTcon, __u32 pid,
+		unsigned long inodeId, __u64 offset, __u64 len, __u8 type)
+{
+	int rc;
+	mutex_lock(&storage_mutex);
+	rc = __cifs_lock_storage_del_lock(xid, pTcon, &LOCK_STORAGE,
+					pid, inodeId, offset, len, type);
+	mutex_unlock(&storage_mutex);
+	return rc;
+}
diff --git a/fs/cifs/cifs_lock_storage.h b/fs/cifs/cifs_lock_storage.h
new file mode 100644
index 0000000..479a019
--- /dev/null
+++ b/fs/cifs/cifs_lock_storage.h
@@ -0,0 +1,38 @@ 
+/*
+ *   fs/cifs/cifs_lock_storage.h
+ *
+ *   lock tree operations that provide posix behaviour on windows server
+ *
+ *   Author(s): Pavel Shilovsky (piastryyy@gmail.com), Copyright (C) 2009.
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CIFS_LOCK_STORAGE
+#define CIFS_LOCK_STORAGE
+
+/* create empty lock tree */
+void cifs_lock_storage_init(void);
+
+/* add lock into lock tree */
+int cifs_lock_storage_add_lock(__u32 pid, unsigned long inodeId,
+		__u16 fid, __u64 offset, __u64 len, __u8 type);
+
+/* delete lock into lock tree */
+int cifs_lock_storage_del_lock(int xid, struct cifsTconInfo *pTcon,
+		__u32 pid, unsigned long inodeId, __u64 offset, __u64 len, __u8 type);
+
+#endif