git » libfilo » commit 57f9c3b

Initial import.

author Alberto Bertogli
2005-04-15 05:22:25 UTC
committer Alberto Bertogli
2005-04-15 05:22:25 UTC

Initial import.

LICENSE +181 -0
Makefile +55 -0
README +4 -0
libfilo.c +456 -0
libfilo.h +60 -0

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6cf9100
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,181 @@
+
+This project, 'libfilo', is copyrighted by Alberto Bertogli and licensed under
+the Open Software License version 2.1 as obtained from www.opensource.org (and
+included here-in for easy reference) (that license itself is copyrighted by
+Larry Rosen).
+
+Note that the "Original Work" that this license covers is only the library
+itself. Thus just the act of linking/importing this library into another
+program does NOT in itself make that program considered a derivative work of
+this Original Work.
+
+		Alberto Bertogli
+		15 April 2005
+
+-------------------------------------------------------------------------
+
+
+Open Software License
+v. 2.1
+
+This Open Software License (the "License") applies to any original work of
+authorship (the "Original Work") whose owner (the "Licensor") has placed the
+following notice immediately following the copyright notice for the Original
+Work:
+
+Licensed under the Open Software License version 2.1
+
+1) Grant of Copyright License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license to do the
+following:
+
+    * to reproduce the Original Work in copies;
+    * to prepare derivative works ("Derivative Works") based upon the Original Work;
+    * to distribute copies of the Original Work and Derivative Works to the
+	public, with the proviso that copies of Original Work or Derivative
+	Works that You distribute shall be licensed under the Open Software
+	License;
+    * to perform the Original Work publicly; and
+    * to display the Original Work publicly. 
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license, under patent
+claims owned or controlled by the Licensor that are embodied in the Original
+Work as furnished by the Licensor, to make, use, sell and offer for sale the
+Original Work and Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the preferred
+form of the Original Work for making modifications to it and all available
+documentation describing how to modify the Original Work. Licensor hereby
+agrees to provide a machine-readable copy of the Source Code of the Original
+Work along with each copy of the Original Work that Licensor distributes.
+Licensor reserves the right to satisfy this obligation by placing a
+machine-readable copy of the Source Code in an information repository
+reasonably calculated to permit inexpensive and convenient access by You for
+as long as Licensor continues to distribute the Original Work, and by
+publishing the address of that information repository in a notice immediately
+following the copyright notice that applies to the Original Work.
+
+4) Exclusions From License Grant. Neither the names of Licensor, nor the names
+of any contributors to the Original Work, nor any of their trademarks or
+service marks, may be used to endorse or promote products derived from this
+Original Work without express prior written permission of the Licensor.
+Nothing in this License shall be deemed to grant any rights to trademarks,
+copyrights, patents, trade secrets or any other intellectual property of
+Licensor except as expressly stated herein. No patent license is granted to
+make, use, sell or offer to sell embodiments of any patent claims other than
+the licensed claims defined in Section 2. No right is granted to the
+trademarks of Licensor even if such marks are included in the Original Work.
+Nothing in this License shall be interpreted to prohibit Licensor from
+licensing under different terms from this License any Original Work that
+Licensor otherwise would have a right to license.
+
+5) External Deployment. The term "External Deployment" means the use or
+distribution of the Original Work or Derivative Works in any way such that the
+Original Work or Derivative Works may be used by anyone other than You,
+whether the Original Work or Derivative Works are distributed to those persons
+or made available as an application intended for use over a computer network.
+As an express condition for the grants of license hereunder, You agree that
+any External Deployment by You of a Derivative Work shall be deemed a
+distribution and shall be licensed to all under the terms of this License, as
+prescribed in section 1(c) herein.
+
+6) Attribution Rights. You must retain, in the Source Code of any Derivative
+Works that You create, all copyright, patent or trademark notices from the
+Source Code of the Original Work, as well as any notices of licensing and any
+descriptive text identified therein as an "Attribution Notice." You must cause
+the Source Code for any Derivative Works that You create to carry a prominent
+Attribution Notice reasonably calculated to inform recipients that You have
+modified the Original Work.
+
+7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
+the copyright in and to the Original Work and the patent rights granted herein
+by Licensor are owned by the Licensor or are sublicensed to You under the
+terms of this License with the permission of the contributor(s) of those
+copyrights and patent rights. Except as expressly stated in the immediately
+proceeding sentence, the Original Work is provided under this License on an
+"AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including,
+without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE
+ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an
+essential part of this License. No license to Original Work is granted
+hereunder except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, shall the
+Licensor be liable to any person for any direct, indirect, special,
+incidental, or consequential damages of any character arising as a result of
+this License or the use of the Original Work including, without limitation,
+damages for loss of goodwill, work stoppage, computer failure or malfunction,
+or any and all other commercial damages or losses. This limitation of
+liability shall not apply to liability for death or personal injury resulting
+from Licensor's negligence to the extent applicable law prohibits such
+limitation. Some jurisdictions do not allow the exclusion or limitation of
+incidental or consequential damages, so this exclusion and limitation may not
+apply to You.
+
+9) Acceptance and Termination. If You distribute copies of the Original Work
+or a Derivative Work, You must make a reasonable effort under the
+circumstances to obtain the express assent of recipients to the terms of this
+License. Nothing else but this License (or another written agreement between
+Licensor and You) grants You permission to create Derivative Works based upon
+the Original Work or to exercise any of the rights granted in Section 1
+herein, and any attempt to do so except under the terms of this License (or
+another written agreement between Licensor and You) is expressly prohibited by
+U.S. copyright law, the equivalent laws of other countries, and by
+international treaty. Therefore, by exercising any of the rights granted to
+You in Section 1 herein, You indicate Your acceptance of this License and all
+of its terms and conditions. This License shall terminate immediately and you
+may no longer exercise any of the rights granted to You by this License upon
+Your failure to honor the proviso in Section 1(c) herein.
+
+10) Termination for Patent Action. This License shall terminate automatically
+and You may no longer exercise any of the rights granted to You by this
+License as of the date You commence an action, including a cross-claim or
+counterclaim, against Licensor or any licensee alleging that the Original Work
+infringes a patent. This termination provision shall not apply for an action
+alleging patent infringement by combinations of the Original Work with other
+software or hardware.
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
+License may be brought only in the courts of a jurisdiction wherein the
+Licensor resides or in which Licensor conducts its primary business, and under
+the laws of that jurisdiction excluding its conflict-of-law provisions. The
+application of the United Nations Convention on Contracts for the
+International Sale of Goods is expressly excluded. Any use of the Original
+Work outside the scope of this License or after its termination shall be
+subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C.
+§ 101 et seq., the equivalent laws of other countries, and international
+treaty. This section shall survive the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License or
+seeking damages relating thereto, the prevailing party shall be entitled to
+recover its costs and expenses, including, without limitation, reasonable
+attorneys' fees and costs incurred in connection with such action, including
+any appeal of such action. This section shall survive the termination of this
+License.
+
+13) Miscellaneous. This License represents the complete agreement concerning
+the subject matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent necessary
+to make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this License,
+whether in upper or lower case, means an individual or a legal entity
+exercising rights under, and complying with all of the terms of, this License.
+For legal entities, "You" includes any entity that controls, is controlled by,
+or is under common control with you. For purposes of this definition,
+"control" means (i) the power, direct or indirect, to cause the direction or
+management of such entity, whether by contract or otherwise, or (ii) ownership
+of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
+ownership of such entity.
+
+15) Right to Use. You may use the Original Work in all ways not otherwise
+restricted or conditioned by this License or by law, and Licensor promises not
+to interfere with or be responsible for such uses by You.
+
+This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights
+reserved. Permission is hereby granted to copy and distribute this license
+without modification. This license may not be modified without the express
+written permission of its copyright owner.
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..130994b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,55 @@
+
+
+CFLAGS += -Wall -D_XOPEN_SOURCE=500
+
+ifdef DEBUG
+	CFLAGS += -g -pg -fprofile-arcs -ftest-coverage
+endif
+
+ifdef STRICT
+CFLAGS += -ansi -pedantic
+endif
+
+# prefix for installing the binaries
+PREFIX=/usr/local
+
+
+OBJS = libfilo.o
+
+default: all
+
+all: libfilo.so libfilo.a
+
+libfilo.so: $(OBJS)
+	$(CC) -shared -fPIC $(OBJS) -o libfilo.so
+
+libfilo.a: $(OBJS)
+	$(AR) cr libfilo.a $(OBJS)
+
+install: all
+	install -d $(PREFIX)/lib
+	install -m 0755 libfilo.so $(PREFIX)/lib
+	install -m 0644 libfilo.a $(PREFIX)/lib
+	install -d $(PREFIX)/include
+	install -m 0644 libfilo.h $(PREFIX)/include
+	@echo
+	@echo "Please run ldconfig to update your library cache"
+	@echo
+
+.c.o:
+	$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+python:
+	cd python && python setup.py build
+
+python_install:
+	cd python && python setup.py install
+
+clean:
+	rm -f $(OBJS) libfilo.a libfilo.so
+	rm -f *.bb *.bbg *.da *.gcov gmon.out
+	rm -rf python/build
+
+
+.PHONY: default all install python python_install clean
+
diff --git a/README b/README
new file mode 100644
index 0000000..beeda86
--- /dev/null
+++ b/README
@@ -0,0 +1,4 @@
+
+Questions or complaints about the name are responsability of Luca
+(luca@llucax.hn.org).
+
diff --git a/libfilo.c b/libfilo.c
new file mode 100644
index 0000000..084c47b
--- /dev/null
+++ b/libfilo.c
@@ -0,0 +1,456 @@
+
+/*
+ * libfilo - A File Locking Library
+ * Alberto Bertogli (albertogli@telpin.com.ar)
+ */
+
+
+#include <sys/types.h>		/* for off_t */
+#include <pthread.h>		/* for mutexes */
+#include <semaphore.h>		/* for semaphores (duh!) */
+#include <stdlib.h>		/* for malloc()/free() */
+#include "libfilo.h"
+
+
+/* We have two lists for each file lock: one of currently locked portions of
+ * the file (a portion can have more than one read lock at the same time, but
+ * only one write lock), and another one with the current waiters for the
+ * lock.
+ * FIXME: document this. everything.
+ */
+
+#if 0			/* XXX: debug */
+#include <stdio.h>
+#define printd(...)				\
+	do {					\
+		fprintf(stderr, "%lu ", pthread_self()); \
+		fprintf(stderr, "%s(): ", __FUNCTION__ ); \
+		fprintf(stderr, __VA_ARGS__);	\
+		fflush(stderr);			\
+	} while(0)
+
+void printfl(filock_t *fl)
+{
+	struct locked_range *lr;
+	struct waiter_range *wr;
+
+	fprintf(stderr, "Filock show\n");
+
+	fprintf(stderr, "Locked:\n");
+	for (lr = fl->locked; lr != NULL; lr = lr->next) {
+		fprintf(stderr, "\t%lu -> %lu (%u) (%lu)\n", lr->start,
+				lr->end, lr->mode, lr->owner);
+	}
+
+	fprintf(stderr, "Waiter:\n");
+	for (wr = fl->waiters; wr != NULL; wr = wr->next) {
+		fprintf(stderr, "\t%lu -> %lu (%u) (%lu)\n", wr->start,
+				wr->end, wr->mode, wr->owner);
+	}
+}
+#else
+	#define printd(...)
+	#define printfl(X)
+#endif
+
+/* Lock/unlock a filock_t */
+#define fl_lock(F) do { pthread_mutex_lock(&(F->lock)); } while (0)
+#define fl_unlock(F) do { pthread_mutex_unlock(&(F->lock)); } while (0)
+
+
+/* Initialize a filock_t structure */
+int filo_init(filock_t *fl)
+{
+	pthread_mutexattr_t attr;
+	pthread_mutexattr_init(&attr);
+	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+	pthread_mutex_init(&(fl->lock), &attr);
+	pthread_mutexattr_destroy(&attr);
+
+	fl->locked = NULL;
+	fl->waiters = NULL;
+	return 1;
+}
+
+/* Frees a filock_t structure */
+int filo_free(filock_t *fl)
+{
+	struct locked_range *lr, *nlr;
+	struct waiter_range *wr, *nwr;
+
+	pthread_mutex_destroy(&(fl->lock));
+
+	for (lr = fl->locked; lr != NULL; lr = nlr) {
+		nlr = lr->next;
+		free(lr);
+	}
+	fl->locked = NULL;
+
+	for (wr = fl->waiters; wr != NULL; wr = nwr) {
+		nwr = wr->next;
+		free(wr);
+	}
+	fl->waiters = NULL;
+
+	return 1;
+}
+
+/* Appends a lock to the filock_t */
+static void locked_append(filock_t *fl, off_t start, off_t end, int mode,
+		pthread_t owner)
+{
+	struct locked_range *lr;
+
+	lr = malloc(sizeof(struct locked_range));
+	lr->start = start;
+	lr->end = end;
+	lr->mode = mode;
+	lr->owner = owner;
+
+	if (fl->locked == NULL) {
+		lr->prev = lr->next = NULL;
+		fl->locked = lr;
+		printd("n: %lu-%lu %d %lu\n", start, end, mode, owner);
+	} else {
+		lr->prev = NULL;
+		fl->locked->prev = lr;
+		lr->next = fl->locked;
+		fl->locked = lr;
+		printd("e: %lu-%lu %d %lu\n", start, end, mode, owner);
+	}
+}
+
+/* Checks if this thread owns the given locked range */
+static int is_ours(struct locked_range *lr)
+{
+	return !!pthread_equal(lr->owner, pthread_self());
+}
+
+/* Checks the given thread owns the given locked range */
+static int is_owner(pthread_t owner, struct locked_range *lr)
+{
+	return !!pthread_equal(lr->owner, owner);
+}
+
+
+/* Checks if the range 1 overlaps with the range 2 */
+static int range_overlaps(off_t start1, off_t end1, off_t start2, off_t end2)
+{
+	if ( (start1 <= start2 && end1 >= end2) ||
+			(start1 >= start2 && start1 <= end2) ||
+			(end1 >= start2 && end1 <= end2) ) {
+		return 1;
+	}
+	return 0;
+}
+
+/* Range overlapping check for locked ranges */
+static int lr_range_overlaps(struct locked_range *lr, off_t start, off_t end)
+{
+	printd("%lu->%lu ov %lu->%lu: %d\n", lr->start, lr->end, start, end, range_overlaps(lr->start, lr->end, start, end));
+	return range_overlaps(lr->start, lr->end, start, end);
+}
+
+/* Range overlapping check for waiter ranges */
+static int wr_range_overlaps(struct waiter_range *wr, off_t start, off_t end)
+{
+	return range_overlaps(wr->start, wr->end, start, end);
+}
+
+
+/* So far we know:
+ *  * there's overlapping between lr and start-end
+ *  * we own lr
+ * We need to solve the problem by doing:
+ *  * If lr is inside start-end, remove it
+ *  * If start-end is inside lr, do an "exclude" and break lr in two
+ *  * If there's partial overlapping, shrink lr not to step on start-end
+ */
+static int exclude_range(filock_t *fl, struct locked_range *lr,
+		off_t start, off_t end)
+{
+	printd("exclude %lu->%lu from %lu->%lu\n", start, end, lr->start, lr->end);
+	if (lr->start >= start && lr->end <= end) {
+		/* it's equal or contained inside, tell to remove */
+		return 0;
+	} else if (lr->start < start && lr->end <= end) {
+		/* we're overlapping from the left, shrink it */
+		printd("shrink end from %lu to %lu\n", lr->end, start - 1);
+		lr->end = start - 1;
+		return 1;
+	} else if (lr->start >= start && lr->end > end) {
+		/* we're overlapping from the right, shrink it */
+		printd("shrink start %lu to %lu\n", lr->start, end + 1);
+		lr->start = end + 1;
+		return 1;
+	} else if (lr->start < start && lr->end > end) {
+		/* start-end is contained inside lr, we need to split lr */
+		locked_append(fl, lr->start, start - 1, lr->mode,
+				pthread_self());
+		locked_append(fl, end + 1, lr->end, lr->mode, pthread_self());
+		/* tell the caller to remove lr */
+		return 0;
+	}
+
+	/* we should NEVER enter here, since all the cases should have been
+	 * covered */
+	return 1;
+}
+
+
+/* Checks if the given thread can lock the given region */
+static int can_lock_tid(filock_t *fl, off_t start, off_t end, int mode,
+		pthread_t owner)
+{
+	struct locked_range *lr;
+
+	for (lr = fl->locked; lr != NULL; lr = lr->next) {
+		/* if we are inside, contained or overlapping somebody else,
+		 * check if:
+		 *  * is ours, so we can play with it freely
+		 *  * is a write lock and we try to read, can't get it
+		 *  * we try to write, can't get it
+		 */
+		if (lr_range_overlaps(lr, start, end)) {
+			if (is_owner(owner, lr)) {
+				/* this check could be moved before the long
+				 * "if" above, but it's an unlikely case and
+				 * the test could get expensive */
+				continue;
+			}
+			if (mode == FL_WR_MODE || lr->mode == FL_WR_MODE) {
+				return 0;
+			}
+		}
+	}
+	printd("can lock it from %lu to %lu as %d\n", start, end, mode);
+	return 1;
+}
+
+/* As can_lock_tid() but gets the owner from the current thread */
+static int can_lock(filock_t *fl, off_t start, off_t end, int mode)
+{
+	return can_lock_tid(fl, start, end, mode, pthread_self());
+}
+
+
+/* Lock a given region, assuming that is not free and we're allowed to own it
+ * (these checks are performed by the caller) */
+static int do_lock_tid(filock_t *fl, off_t start, off_t end, int mode,
+		pthread_t owner)
+{
+	struct locked_range *lr, *next;
+
+	for (lr = fl->locked; lr != NULL; lr = next) {
+		next = lr->next;
+
+		if (!is_ours(lr))
+			continue;
+		if (!lr_range_overlaps(lr, start, end))
+			continue;
+
+		printd("overlap %lu %lu %d %lu\n", start, end, mode, owner);
+		if (exclude_range(fl, lr, start, end) == 0) {
+			printd("rem lr %lu->%lu\n", lr->start, lr->end);
+			/* we need to remove lr */
+			if (fl->locked == lr)
+				fl->locked = lr->next;
+			if (lr->prev != NULL)
+				lr->prev->next = lr->next;
+			if (lr->next != NULL)
+				lr->next->prev = lr->prev;
+			free(lr);
+		}
+	}
+
+	/* now the region is safe to do this */
+	locked_append(fl, start, end, mode, owner);
+	return 1;
+}
+
+
+/* Same as do_lock_tid(), but gets the tid from the currently running thread;
+ * it's a shortcut for a lot of uses */
+static int do_lock(filock_t *fl, off_t start, off_t end, int mode)
+{
+	return do_lock_tid(fl, start, end, mode, pthread_self());
+}
+
+
+/* Check if a region is free from locks so we can add the lock right in,
+ * skipping all the other tests */
+static int region_free(filock_t *fl, off_t start, off_t end) {
+	struct locked_range *lr;
+	for (lr = fl->locked; lr != NULL; lr = lr->next) {
+		if (lr_range_overlaps(lr, start, end))
+			return 0;
+	}
+	return 1;
+}
+
+/* Tries to get a lock, returns 1 if success or 0 if failure */
+int filo_trylock(filock_t *fl, off_t start, off_t len, int mode)
+{
+	int rv = 0;
+	int end = start + len;
+
+	fl_lock(fl);
+
+	/* fast path for the uncontended case */
+	if (fl->locked == NULL) {
+		locked_append(fl, start, end, mode, pthread_self());
+		rv = 1;
+	} else if (region_free(fl, start, end)) {
+		locked_append(fl, start, end, mode, pthread_self());
+		rv = 1;
+	} else if (can_lock(fl, start, end, mode)) {
+		do_lock(fl, start, end, mode);
+		rv = 1;
+	} else {
+		/* the region is busy, we would lock */
+		rv = 0;
+	}
+
+	fl_unlock(fl);
+	return rv;
+}
+
+
+static void wait_on(filock_t *fl, off_t start, off_t end, int mode)
+{
+	/* all waiter_ranges live on the heap and are created and destroyed
+	 * here. A wr is born here, go to bed in the locking below, until
+	 * wake_up() wakes us up and he dies. While it is manipulated several
+	 * times outside this function, here is the only place where it's
+	 * actually modified. */
+	struct waiter_range wr;
+
+	/* initialize wr */
+	wr.start = start;
+	wr.end = end;
+	wr.mode = mode;
+	wr.owner = pthread_self();
+	wr.next = NULL;
+	wr.prev = NULL;
+
+	sem_init(&(wr.sem), 0, 0);
+
+	/* add to the list */
+	if (fl->waiters == NULL) {
+		wr.next = wr.prev = NULL;
+		fl->waiters = &wr;
+	} else {
+		wr.prev = NULL;
+		fl->waiters->prev = &wr;
+		wr.next = fl->waiters;
+		fl->waiters = &wr;
+	}
+
+	fl_unlock(fl);
+
+	/* block until some other thread wakes us up */
+	sem_wait(&(wr.sem));
+}
+
+static void wake_up(filock_t *fl, off_t start, off_t end)
+{
+	struct waiter_range *wr;
+
+	for (wr = fl->waiters; wr != NULL; wr = wr->next) {
+		if (!wr_range_overlaps(wr, start, end))
+			/* if the region doesn't overlap, we know beforehand
+			 * the status couldn't have changed */
+			continue;
+		if (can_lock_tid(fl, wr->start, wr->end, wr->mode, wr->owner)) {
+			do_lock_tid(fl, wr->start, wr->end, wr->mode,
+					wr->owner);
+
+			/* remove from the list */
+			if (fl->waiters == wr)
+				fl->waiters = wr->next;
+			if (wr->prev != NULL)
+				wr->prev->next = wr->next;
+			if (wr->next != NULL)
+				wr->next->prev = wr->prev;
+			/* it lives on wait_on()'s stack, there's no need to
+			 * free it */
+
+			/* this will wake the thread up (and the thread will
+			 * destroy wr) */
+			sem_post(&(wr->sem));
+
+			return;
+		}
+	}
+
+	/* nobody was waiting! */
+	return;
+}
+
+
+/* Get a lock, blocking until available */
+int filo_lock(filock_t *fl, off_t start, off_t len, int mode)
+{
+	off_t end = start + len;
+
+	fl_lock(fl);
+
+	/* fast path for the uncontended case */
+	if (fl->locked == NULL) {
+		locked_append(fl, start, end, mode, pthread_self());
+		fl_unlock(fl);
+	} else if (region_free(fl, start, end)) {
+		locked_append(fl, start, end, mode, pthread_self());
+		fl_unlock(fl);
+	} else if (can_lock(fl, start, end, mode)) {
+		printd("locking\n");
+		do_lock(fl, start, end, mode);
+		fl_unlock(fl);
+	} else {
+		/* the region is busy, we wait */
+		wait_on(fl, start, end, mode);
+		/* when we return, fl->lock has already been unlocked, there's
+		 * no need to release it */
+	}
+
+	printfl(fl);
+	return 1;
+}
+
+
+int filo_unlock(filock_t *fl, off_t start, off_t len)
+{
+	struct locked_range *lr, *next;
+	off_t end = start + len;
+
+	fl_lock(fl);
+
+	for (lr = fl->locked; lr != NULL; lr = next) {
+		next = lr->next;
+
+		if (!is_ours(lr))
+			continue;
+		if (!lr_range_overlaps(lr, start, end))
+			continue;
+
+		/* the range is ours and overlaps, unlock this area */
+		if (exclude_range(fl, lr, start, end) == 0) {
+			/* we need to remove lr */
+			if (fl->locked == lr)
+				fl->locked = lr->next;
+			if (lr->prev != NULL)
+				lr->prev->next = lr->next;
+			if (lr->next != NULL)
+				lr->next->prev = lr->prev;
+			free(lr);
+		}
+	}
+
+	wake_up(fl, start, end);
+	printd("UNLOCKED\n");
+	printfl(fl);
+	fl_unlock(fl);
+	return 1;
+}
+
+
diff --git a/libfilo.h b/libfilo.h
new file mode 100644
index 0000000..6e65fbb
--- /dev/null
+++ b/libfilo.h
@@ -0,0 +1,60 @@
+
+/*
+ * libfilo - A File Locking Library
+ * Alberto Bertogli (albertogli@telpin.com.ar)
+ */
+
+
+#ifndef _LIBFILO_H
+#define _LIBFILO_H
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <unistd.h>	/* because filo_plockf() uses the same constants as
+			 * lockf() */
+
+
+/* locking modes */
+#define FL_RD_MODE 0
+#define FL_WR_MODE 1
+
+
+/* a single locked range */
+struct locked_range {
+	off_t start;
+	off_t end;
+	int mode;
+	pthread_t owner;
+	struct locked_range *next;
+	struct locked_range *prev;
+};
+
+/* a range waiting for read or write */
+struct waiter_range {
+	off_t start;
+	off_t end;
+	int mode;
+	pthread_t owner;
+	sem_t sem;
+	struct waiter_range *next;
+	struct waiter_range *prev;
+};
+
+/* a file lock */
+typedef struct filo_flock_t {
+	struct locked_range *locked;
+	struct waiter_range *waiters;
+	pthread_mutex_t lock;
+} filock_t;
+
+
+int filo_init(filock_t *fl);
+int filo_free(filock_t *fl);
+int filo_lock(filock_t *fl, off_t start, off_t len, int mode);
+int filo_trylock(filock_t *fl, off_t start, off_t len, int mode);
+int filo_unlock(filock_t *fl, off_t start, off_t len);
+int filo_islocked(filock_t *fl, off_t start, off_t len);
+int filo_plockf(filock_t *fl, int cmd, off_t start, off_t len);
+
+#endif
+