mirror of
https://github.com/libfuse/libfuse.git
synced 2024-11-27 06:04:27 +08:00
ulockmgr: strip ulockmgr support from this source package
Distribute ulockmgr separately. It is not needed for the building of libfuse, only fusexmp_fh. Check ulockmgr library in ./configure and if not disable remote-lock suport in fusexmp_fh.
This commit is contained in:
parent
0cb2db32e7
commit
3c3f03b81f
@ -5,6 +5,11 @@
|
||||
libfuse3.*. Invoke pkg-config with "fuse3" as the first argument
|
||||
to build with version 3 of the library.
|
||||
|
||||
* ulockmgr: strip ulockmgr support from this source package and
|
||||
distribute it separately. It is not needed for the building of
|
||||
libfuse, only fusexmp_fh. Check ulockmgr library in ./configure
|
||||
and if not disable remote-lock suport in fusexmp_fh.
|
||||
|
||||
2013-07-24 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* libfuse: remove "-D_FILE_OFFSET_BITS=64" from fuse.pc, add
|
||||
|
@ -63,6 +63,11 @@ AC_SEARCH_LIBS(dlopen, [dl])
|
||||
AC_SEARCH_LIBS(clock_gettime, [rt])
|
||||
libfuse_libs=$LIBS
|
||||
LIBS=
|
||||
AC_CHECK_LIB(ulockmgr, ulockmgr_op)
|
||||
fusexmp_fh_libs=$LIBS
|
||||
AC_SUBST(fusexmp_fh_libs)
|
||||
LIBS=
|
||||
|
||||
AC_ARG_WITH([libiconv-prefix],
|
||||
[ --with-libiconv-prefix=DIR search for libiconv in DIR/include and DIR/lib], [
|
||||
for dir in `echo "$withval" | tr : ' '`; do
|
||||
|
@ -1,5 +1,5 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
dist_man_MANS = fusermount.1 mount.fuse.8 ulockmgr_server.1
|
||||
dist_man_MANS = fusermount.1 mount.fuse.8
|
||||
|
||||
EXTRA_DIST = how-fuse-works kernel.txt Doxyfile
|
||||
|
@ -1,28 +0,0 @@
|
||||
.TH ULOCKMGR_SERVER 1 2011\-10\-23 2.8.6 "Filesystem in Userspace (FUSE)"
|
||||
|
||||
.SH NAME
|
||||
\fBulockmgr_server\fR \- Lock Manager Server for FUSE filesystems
|
||||
|
||||
.SH SYNOPSIS
|
||||
\fBulockmgr_server\fR
|
||||
|
||||
.SH DESCRIPTION
|
||||
Filesystem in Userspace (FUSE) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. It also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations.
|
||||
.PP
|
||||
\fBulockmgr_server\fR is the Userspace Lock Manager Server for FUSE filesystems.
|
||||
|
||||
.SH OPTIONS
|
||||
\fBulockmgr_server\fR has no options.
|
||||
|
||||
.SH SEE ALSO
|
||||
\fIfusermount\fR(1),
|
||||
\fImount\fR(8),
|
||||
\fImount.fuse\fR(8).
|
||||
|
||||
.SH HOMEPAGE
|
||||
More information about ulockmgr_server and the FUSE project can be found at <\fIhttp://fuse.sourceforge.net/\fR>.
|
||||
|
||||
.SH AUTHOR
|
||||
FUSE was written by Miklos Szeredi <\fImiklos@szeredi.hu\fR>.
|
||||
.PP
|
||||
This manual page was written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>.
|
@ -6,7 +6,7 @@ noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll fioc fioclient \
|
||||
fsel fselclient cusexmp
|
||||
|
||||
LDADD = ../lib/libfuse3.la
|
||||
fusexmp_fh_LDADD = ../lib/libfuse3.la ../lib/libulockmgr.la
|
||||
fusexmp_fh_LDADD = ../lib/libfuse3.la @fusexmp_fh_libs@
|
||||
|
||||
fioclient_CPPFLAGS =
|
||||
fioclient_LDFLAGS =
|
||||
|
@ -29,7 +29,11 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <fuse.h>
|
||||
|
||||
#ifdef HAVE_LIBULOCKMGR
|
||||
#include <ulockmgr.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -500,6 +504,7 @@ static int xmp_removexattr(const char *path, const char *name)
|
||||
}
|
||||
#endif /* HAVE_SETXATTR */
|
||||
|
||||
#ifdef HAVE_LIBULOCKMGR
|
||||
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
|
||||
struct flock *lock)
|
||||
{
|
||||
@ -508,6 +513,7 @@ static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
|
||||
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
|
||||
sizeof(fi->lock_owner));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
|
||||
{
|
||||
@ -562,7 +568,9 @@ static struct fuse_operations xmp_oper = {
|
||||
.listxattr = xmp_listxattr,
|
||||
.removexattr = xmp_removexattr,
|
||||
#endif
|
||||
#ifdef HAVE_LIBULOCKMGR
|
||||
.lock = xmp_lock,
|
||||
#endif
|
||||
.flock = xmp_flock,
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,4 @@ fuseinclude_HEADERS = \
|
||||
fuse_opt.h \
|
||||
cuse_lowlevel.h
|
||||
|
||||
include_HEADERS = ulockmgr.h
|
||||
|
||||
noinst_HEADERS = fuse_kernel.h
|
||||
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
libulockmgr: Userspace Lock Manager Library
|
||||
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/**
|
||||
* Perform POSIX locking operation
|
||||
*
|
||||
* @param fd the file descriptor
|
||||
* @param cmd the locking command (F_GETFL, F_SETLK or F_SETLKW)
|
||||
* @param lock the lock parameters
|
||||
* @param owner the lock owner ID cookie
|
||||
* @param owner_len length of the lock owner ID cookie
|
||||
* @return 0 on success -errno on error
|
||||
*/
|
||||
int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
|
||||
size_t owner_len);
|
@ -3,7 +3,7 @@
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -DFUSERMOUNT_DIR=\"$(bindir)\" \
|
||||
-D_REENTRANT -DFUSE_USE_VERSION=30
|
||||
|
||||
lib_LTLIBRARIES = libfuse3.la libulockmgr.la
|
||||
lib_LTLIBRARIES = libfuse3.la
|
||||
|
||||
if BSD
|
||||
mount_source = mount_bsd.c
|
||||
@ -42,7 +42,4 @@ if NETBSD
|
||||
libfuse3_la_LIBADD = -lperfuse -lpuffs
|
||||
endif
|
||||
|
||||
libulockmgr_la_SOURCES = ulockmgr.c
|
||||
libulockmgr_la_LDFLAGS = -pthread -version-number 1:0:1
|
||||
|
||||
EXTRA_DIST = fuse_versionscript
|
||||
|
445
lib/ulockmgr.c
445
lib/ulockmgr.c
@ -1,445 +0,0 @@
|
||||
/*
|
||||
libulockmgr: Userspace Lock Manager Library
|
||||
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
|
||||
#include "config.h"
|
||||
#include "ulockmgr.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
struct message {
|
||||
unsigned intr : 1;
|
||||
unsigned nofd : 1;
|
||||
pthread_t thr;
|
||||
int cmd;
|
||||
int fd;
|
||||
struct flock lock;
|
||||
int error;
|
||||
};
|
||||
|
||||
struct fd_store {
|
||||
struct fd_store *next;
|
||||
int fd;
|
||||
int inuse;
|
||||
};
|
||||
|
||||
struct owner {
|
||||
struct owner *next;
|
||||
struct owner *prev;
|
||||
struct fd_store *fds;
|
||||
void *id;
|
||||
size_t id_len;
|
||||
int cfd;
|
||||
};
|
||||
|
||||
static pthread_mutex_t ulockmgr_lock;
|
||||
static int ulockmgr_cfd = -1;
|
||||
static struct owner owner_list = { .next = &owner_list, .prev = &owner_list };
|
||||
|
||||
#define MAX_SEND_FDS 2
|
||||
|
||||
static void list_del_owner(struct owner *owner)
|
||||
{
|
||||
struct owner *prev = owner->prev;
|
||||
struct owner *next = owner->next;
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
}
|
||||
|
||||
static void list_add_owner(struct owner *owner, struct owner *next)
|
||||
{
|
||||
struct owner *prev = next->prev;
|
||||
owner->next = next;
|
||||
owner->prev = prev;
|
||||
prev->next = owner;
|
||||
next->prev = owner;
|
||||
}
|
||||
|
||||
/*
|
||||
* There's a bug in the linux kernel (< 2.6.22) recv() implementation
|
||||
* on AF_UNIX, SOCK_STREAM sockets, that could cause it to return
|
||||
* zero, even if data was available. Retrying the recv will return
|
||||
* the data in this case.
|
||||
*/
|
||||
static int do_recv(int sock, void *buf, size_t len, int flags)
|
||||
{
|
||||
int res = recv(sock, buf, len, flags);
|
||||
if (res == 0)
|
||||
res = recv(sock, buf, len, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ulockmgr_send_message(int sock, void *buf, size_t buflen,
|
||||
int *fdp, int numfds)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *p_cmsg;
|
||||
struct iovec vec;
|
||||
size_t cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
|
||||
int res;
|
||||
|
||||
assert(numfds <= MAX_SEND_FDS);
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
p_cmsg = CMSG_FIRSTHDR(&msg);
|
||||
p_cmsg->cmsg_level = SOL_SOCKET;
|
||||
p_cmsg->cmsg_type = SCM_RIGHTS;
|
||||
p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds);
|
||||
memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds);
|
||||
msg.msg_controllen = p_cmsg->cmsg_len;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
vec.iov_base = buf;
|
||||
vec.iov_len = buflen;
|
||||
res = sendmsg(sock, &msg, MSG_NOSIGNAL);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: sendmsg");
|
||||
return -1;
|
||||
}
|
||||
if ((size_t) res != buflen) {
|
||||
fprintf(stderr, "libulockmgr: sendmsg short\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ulockmgr_start_daemon(void)
|
||||
{
|
||||
int sv[2];
|
||||
int res;
|
||||
char tmp[64];
|
||||
|
||||
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: socketpair");
|
||||
return -1;
|
||||
}
|
||||
snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]);
|
||||
res = system(tmp);
|
||||
close(sv[0]);
|
||||
if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) {
|
||||
close(sv[1]);
|
||||
return -1;
|
||||
}
|
||||
ulockmgr_cfd = sv[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct owner *ulockmgr_new_owner(const void *id, size_t id_len)
|
||||
{
|
||||
int sv[2];
|
||||
int res;
|
||||
char c = 'm';
|
||||
struct owner *o;
|
||||
|
||||
if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1)
|
||||
return NULL;
|
||||
|
||||
o = calloc(1, sizeof(struct owner) + id_len);
|
||||
if (!o) {
|
||||
fprintf(stderr, "libulockmgr: failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
o->id = o + 1;
|
||||
o->id_len = id_len;
|
||||
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: socketpair");
|
||||
goto out_free;
|
||||
}
|
||||
res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1);
|
||||
close(sv[0]);
|
||||
if (res == -1) {
|
||||
close(ulockmgr_cfd);
|
||||
ulockmgr_cfd = -1;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
o->cfd = sv[1];
|
||||
memcpy(o->id, id, id_len);
|
||||
list_add_owner(o, &owner_list);
|
||||
|
||||
return o;
|
||||
|
||||
out_close:
|
||||
close(sv[1]);
|
||||
out_free:
|
||||
free(o);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ulockmgr_send_request(struct message *msg, const void *id,
|
||||
size_t id_len)
|
||||
{
|
||||
int sv[2];
|
||||
int cfd;
|
||||
struct owner *o;
|
||||
struct fd_store *f = NULL;
|
||||
struct fd_store *newf = NULL;
|
||||
struct fd_store **fp;
|
||||
int fd = msg->fd;
|
||||
int cmd = msg->cmd;
|
||||
int res;
|
||||
int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
|
||||
msg->lock.l_start == 0 && msg->lock.l_len == 0);
|
||||
|
||||
for (o = owner_list.next; o != &owner_list; o = o->next)
|
||||
if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0)
|
||||
break;
|
||||
|
||||
if (o == &owner_list)
|
||||
o = NULL;
|
||||
|
||||
if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK)
|
||||
o = ulockmgr_new_owner(id, id_len);
|
||||
|
||||
if (!o) {
|
||||
if (cmd == F_GETLK) {
|
||||
res = fcntl(msg->fd, F_GETLK, &msg->lock);
|
||||
return (res == -1) ? -errno : 0;
|
||||
} else if (msg->lock.l_type == F_UNLCK)
|
||||
return 0;
|
||||
else
|
||||
return -ENOLCK;
|
||||
}
|
||||
|
||||
if (unlockall)
|
||||
msg->nofd = 1;
|
||||
else {
|
||||
for (fp = &o->fds; *fp; fp = &(*fp)->next) {
|
||||
f = *fp;
|
||||
if (f->fd == fd) {
|
||||
msg->nofd = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!msg->nofd) {
|
||||
newf = f = calloc(1, sizeof(struct fd_store));
|
||||
if (!f) {
|
||||
fprintf(stderr, "libulockmgr: failed to allocate memory\n");
|
||||
return -ENOLCK;
|
||||
}
|
||||
}
|
||||
|
||||
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: socketpair");
|
||||
free(newf);
|
||||
return -ENOLCK;
|
||||
}
|
||||
|
||||
cfd = sv[1];
|
||||
sv[1] = msg->fd;
|
||||
res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv,
|
||||
msg->nofd ? 1 : 2);
|
||||
close(sv[0]);
|
||||
if (res == -1) {
|
||||
free(newf);
|
||||
close(cfd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (newf) {
|
||||
newf->fd = msg->fd;
|
||||
newf->next = o->fds;
|
||||
o->fds = newf;
|
||||
}
|
||||
if (f)
|
||||
f->inuse++;
|
||||
|
||||
res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: recv");
|
||||
msg->error = EIO;
|
||||
} else if (res != sizeof(struct message)) {
|
||||
fprintf(stderr, "libulockmgr: recv short\n");
|
||||
msg->error = EIO;
|
||||
} else if (cmd == F_SETLKW && msg->error == EAGAIN) {
|
||||
pthread_mutex_unlock(&ulockmgr_lock);
|
||||
while (1) {
|
||||
sigset_t old;
|
||||
sigset_t unblock;
|
||||
int errno_save;
|
||||
|
||||
sigemptyset(&unblock);
|
||||
sigaddset(&unblock, SIGUSR1);
|
||||
pthread_sigmask(SIG_UNBLOCK, &unblock, &old);
|
||||
res = do_recv(cfd, msg, sizeof(struct message),
|
||||
MSG_WAITALL);
|
||||
errno_save = errno;
|
||||
pthread_sigmask(SIG_SETMASK, &old, NULL);
|
||||
if (res == sizeof(struct message))
|
||||
break;
|
||||
else if (res >= 0) {
|
||||
fprintf(stderr, "libulockmgr: recv short\n");
|
||||
msg->error = EIO;
|
||||
break;
|
||||
} else if (errno_save != EINTR) {
|
||||
errno = errno_save;
|
||||
perror("libulockmgr: recv");
|
||||
msg->error = EIO;
|
||||
break;
|
||||
}
|
||||
msg->intr = 1;
|
||||
res = send(o->cfd, msg, sizeof(struct message),
|
||||
MSG_NOSIGNAL);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: send");
|
||||
msg->error = EIO;
|
||||
break;
|
||||
}
|
||||
if (res != sizeof(struct message)) {
|
||||
fprintf(stderr, "libulockmgr: send short\n");
|
||||
msg->error = EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_lock(&ulockmgr_lock);
|
||||
|
||||
}
|
||||
if (f)
|
||||
f->inuse--;
|
||||
close(cfd);
|
||||
if (unlockall) {
|
||||
for (fp = &o->fds; *fp;) {
|
||||
f = *fp;
|
||||
if (f->fd == fd && !f->inuse) {
|
||||
*fp = f->next;
|
||||
free(f);
|
||||
} else
|
||||
fp = &f->next;
|
||||
}
|
||||
if (!o->fds) {
|
||||
list_del_owner(o);
|
||||
close(o->cfd);
|
||||
free(o);
|
||||
}
|
||||
/* Force OK on unlock-all, since it _will_ succeed once the
|
||||
owner is deleted */
|
||||
msg->error = 0;
|
||||
}
|
||||
|
||||
return -msg->error;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static uint32_t owner_hash(const unsigned char *id, size_t id_len)
|
||||
{
|
||||
uint32_t h = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < id_len; i++)
|
||||
h = ((h << 8) | (h >> 24)) ^ id[i];
|
||||
|
||||
return h;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ulockmgr_canonicalize(int fd, struct flock *lock)
|
||||
{
|
||||
off_t offset;
|
||||
if (lock->l_whence == SEEK_CUR) {
|
||||
offset = lseek(fd, 0, SEEK_CUR);
|
||||
if (offset == (off_t) -1)
|
||||
return -errno;
|
||||
} else if (lock->l_whence == SEEK_END) {
|
||||
struct stat stbuf;
|
||||
int res = fstat(fd, &stbuf);
|
||||
if (res == -1)
|
||||
return -errno;
|
||||
|
||||
offset = stbuf.st_size;
|
||||
} else
|
||||
offset = 0;
|
||||
|
||||
lock->l_whence = SEEK_SET;
|
||||
lock->l_start += offset;
|
||||
|
||||
if (lock->l_start < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (lock->l_len < 0) {
|
||||
lock->l_start += lock->l_len;
|
||||
if (lock->l_start < 0)
|
||||
return -EINVAL;
|
||||
lock->l_len = -lock->l_len;
|
||||
}
|
||||
if (lock->l_len && lock->l_start + lock->l_len - 1 < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
|
||||
size_t owner_len)
|
||||
{
|
||||
int err;
|
||||
struct message msg;
|
||||
sigset_t old;
|
||||
sigset_t block;
|
||||
|
||||
if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW)
|
||||
return -EINVAL;
|
||||
|
||||
if (lock->l_type != F_RDLCK && lock->l_type != F_WRLCK &&
|
||||
lock->l_type != F_UNLCK)
|
||||
return -EINVAL;
|
||||
|
||||
if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
|
||||
lock->l_whence != SEEK_END)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
|
||||
cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len,
|
||||
owner_hash(owner, owner_len));
|
||||
#endif
|
||||
|
||||
/* Unlock should never block anyway */
|
||||
if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
|
||||
cmd = F_SETLK;
|
||||
|
||||
memset(&msg, 0, sizeof(struct message));
|
||||
msg.cmd = cmd;
|
||||
msg.fd = fd;
|
||||
msg.lock = *lock;
|
||||
err = ulockmgr_canonicalize(fd, &msg.lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sigemptyset(&block);
|
||||
sigaddset(&block, SIGUSR1);
|
||||
pthread_sigmask(SIG_BLOCK, &block, &old);
|
||||
pthread_mutex_lock(&ulockmgr_lock);
|
||||
err = ulockmgr_send_request(&msg, owner, owner_len);
|
||||
pthread_mutex_unlock(&ulockmgr_lock);
|
||||
pthread_sigmask(SIG_SETMASK, &old, NULL);
|
||||
if (!err && cmd == F_GETLK) {
|
||||
if (msg.lock.l_type == F_UNLCK)
|
||||
lock->l_type = F_UNLCK;
|
||||
else
|
||||
*lock = msg.lock;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
1
util/.gitignore
vendored
1
util/.gitignore
vendored
@ -1,5 +1,4 @@
|
||||
fusermount
|
||||
ulockmgr_server
|
||||
fuse_ioslave
|
||||
mount.fuse
|
||||
mount_util.c
|
||||
|
@ -1,6 +1,6 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
bin_PROGRAMS = fusermount ulockmgr_server
|
||||
bin_PROGRAMS = fusermount
|
||||
noinst_PROGRAMS = mount.fuse
|
||||
|
||||
# we re-use mount_util.c from the library, but do want to keep ourself
|
||||
@ -15,10 +15,6 @@ mount_util.c: $(top_srcdir)/lib/mount_util.c
|
||||
|
||||
mount_fuse_SOURCES = mount.fuse.c
|
||||
|
||||
ulockmgr_server_SOURCES = ulockmgr_server.c
|
||||
ulockmgr_server_CPPFLAGS = -D_REENTRANT
|
||||
ulockmgr_server_LDFLAGS = -pthread
|
||||
|
||||
install-exec-hook:
|
||||
-chmod u+s $(DESTDIR)$(bindir)/fusermount
|
||||
@if test ! -e $(DESTDIR)/dev/fuse; then \
|
||||
|
@ -1,426 +0,0 @@
|
||||
/*
|
||||
ulockmgr_server: Userspace Lock Manager Server
|
||||
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
struct message {
|
||||
unsigned intr : 1;
|
||||
unsigned nofd : 1;
|
||||
pthread_t thr;
|
||||
int cmd;
|
||||
int fd;
|
||||
struct flock lock;
|
||||
int error;
|
||||
};
|
||||
|
||||
struct fd_store {
|
||||
struct fd_store *next;
|
||||
int fd;
|
||||
int origfd;
|
||||
int inuse;
|
||||
};
|
||||
|
||||
struct owner {
|
||||
struct fd_store *fds;
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
struct req_data {
|
||||
struct owner *o;
|
||||
int cfd;
|
||||
struct fd_store *f;
|
||||
struct message msg;
|
||||
};
|
||||
|
||||
#define MAX_SEND_FDS 2
|
||||
|
||||
static int receive_message(int sock, void *buf, size_t buflen, int *fdp,
|
||||
int *numfds)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
size_t ccmsg[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
|
||||
struct cmsghdr *cmsg;
|
||||
int res;
|
||||
int i;
|
||||
|
||||
assert(*numfds <= MAX_SEND_FDS);
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = buflen;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(ccmsg, -1, sizeof(ccmsg));
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = ccmsg;
|
||||
msg.msg_controllen = sizeof(ccmsg);
|
||||
|
||||
res = recvmsg(sock, &msg, MSG_WAITALL);
|
||||
if (!res) {
|
||||
/* retry on zero return, see do_recv() in ulockmgr.c */
|
||||
res = recvmsg(sock, &msg, MSG_WAITALL);
|
||||
if (!res)
|
||||
return 0;
|
||||
}
|
||||
if (res == -1) {
|
||||
perror("ulockmgr_server: recvmsg");
|
||||
return -1;
|
||||
}
|
||||
if ((size_t) res != buflen) {
|
||||
fprintf(stderr, "ulockmgr_server: short message received\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg) {
|
||||
if (!cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: unknown control message %d\n",
|
||||
cmsg->cmsg_type);
|
||||
return -1;
|
||||
}
|
||||
memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
|
||||
if (msg.msg_flags & MSG_CTRUNC) {
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: control message truncated\n");
|
||||
for (i = 0; i < *numfds; i++)
|
||||
close(fdp[i]);
|
||||
*numfds = 0;
|
||||
}
|
||||
} else {
|
||||
if (msg.msg_flags & MSG_CTRUNC) {
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: control message truncated(*)\n");
|
||||
|
||||
/* There's a bug in the Linux kernel, that if
|
||||
not all file descriptors were allocated,
|
||||
then the cmsg header is not filled in */
|
||||
cmsg = (struct cmsghdr *) ccmsg;
|
||||
memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
|
||||
for (i = 0; i < *numfds; i++)
|
||||
close(fdp[i]);
|
||||
}
|
||||
*numfds = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int closefrom(int minfd)
|
||||
{
|
||||
DIR *dir = opendir("/proc/self/fd");
|
||||
if (dir) {
|
||||
int dfd = dirfd(dir);
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir))) {
|
||||
char *end;
|
||||
int fd = strtol(ent->d_name, &end, 10);
|
||||
if (ent->d_name[0] && !end[0] && fd >= minfd &&
|
||||
fd != dfd)
|
||||
close(fd);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void send_reply(int cfd, struct message *msg)
|
||||
{
|
||||
int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL);
|
||||
if (res == -1)
|
||||
perror("ulockmgr_server: sending reply");
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *process_request(void *d_)
|
||||
{
|
||||
struct req_data *d = d_;
|
||||
int res;
|
||||
|
||||
assert(d->msg.cmd == F_SETLKW);
|
||||
res = fcntl(d->f->fd, F_SETLK, &d->msg.lock);
|
||||
if (res == -1 && errno == EAGAIN) {
|
||||
d->msg.error = EAGAIN;
|
||||
d->msg.thr = pthread_self();
|
||||
send_reply(d->cfd, &d->msg);
|
||||
res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock);
|
||||
}
|
||||
d->msg.error = (res == -1) ? errno : 0;
|
||||
pthread_mutex_lock(&d->o->lock);
|
||||
d->f->inuse--;
|
||||
pthread_mutex_unlock(&d->o->lock);
|
||||
send_reply(d->cfd, &d->msg);
|
||||
close(d->cfd);
|
||||
free(d);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void process_message(struct owner *o, struct message *msg, int cfd,
|
||||
int fd)
|
||||
{
|
||||
struct fd_store *f = NULL;
|
||||
struct fd_store *newf = NULL;
|
||||
struct fd_store **fp;
|
||||
struct req_data *d;
|
||||
pthread_t tid;
|
||||
int res;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n",
|
||||
msg->cmd, msg->lock.l_type, msg->lock.l_whence,
|
||||
msg->lock.l_start, msg->lock.l_len);
|
||||
#endif
|
||||
|
||||
if (msg->cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
|
||||
msg->lock.l_start == 0 && msg->lock.l_len == 0) {
|
||||
for (fp = &o->fds; *fp;) {
|
||||
f = *fp;
|
||||
if (f->origfd == msg->fd && !f->inuse) {
|
||||
close(f->fd);
|
||||
*fp = f->next;
|
||||
free(f);
|
||||
} else
|
||||
fp = &f->next;
|
||||
}
|
||||
if (!msg->nofd)
|
||||
close(fd);
|
||||
|
||||
msg->error = 0;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg->nofd) {
|
||||
for (fp = &o->fds; *fp; fp = &(*fp)->next) {
|
||||
f = *fp;
|
||||
if (f->origfd == msg->fd)
|
||||
break;
|
||||
}
|
||||
if (!*fp) {
|
||||
fprintf(stderr, "ulockmgr_server: fd %i not found\n",
|
||||
msg->fd);
|
||||
msg->error = EIO;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
newf = f = malloc(sizeof(struct fd_store));
|
||||
if (!f) {
|
||||
msg->error = ENOLCK;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
return;
|
||||
}
|
||||
|
||||
f->fd = fd;
|
||||
f->origfd = msg->fd;
|
||||
f->inuse = 0;
|
||||
}
|
||||
|
||||
if (msg->cmd == F_GETLK || msg->cmd == F_SETLK ||
|
||||
msg->lock.l_type == F_UNLCK) {
|
||||
res = fcntl(f->fd, msg->cmd, &msg->lock);
|
||||
msg->error = (res == -1) ? errno : 0;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
if (newf) {
|
||||
newf->next = o->fds;
|
||||
o->fds = newf;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
d = malloc(sizeof(struct req_data));
|
||||
if (!d) {
|
||||
msg->error = ENOLCK;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
free(newf);
|
||||
return;
|
||||
}
|
||||
|
||||
f->inuse++;
|
||||
d->o = o;
|
||||
d->cfd = cfd;
|
||||
d->f = f;
|
||||
d->msg = *msg;
|
||||
res = pthread_create(&tid, NULL, process_request, d);
|
||||
if (res) {
|
||||
msg->error = ENOLCK;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
free(d);
|
||||
f->inuse--;
|
||||
free(newf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newf) {
|
||||
newf->next = o->fds;
|
||||
o->fds = newf;
|
||||
}
|
||||
pthread_detach(tid);
|
||||
}
|
||||
|
||||
static void sigusr1_handler(int sig)
|
||||
{
|
||||
(void) sig;
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
static void process_owner(int cfd)
|
||||
{
|
||||
struct owner o;
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = sigusr1_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
|
||||
perror("ulockmgr_server: cannot set sigusr1 signal handler");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&o, 0, sizeof(struct owner));
|
||||
pthread_mutex_init(&o.lock, NULL);
|
||||
while (1) {
|
||||
struct message msg;
|
||||
int rfds[2];
|
||||
int res;
|
||||
int numfds = 2;
|
||||
|
||||
res = receive_message(cfd, &msg, sizeof(msg), rfds, &numfds);
|
||||
if (!res)
|
||||
break;
|
||||
if (res == -1)
|
||||
exit(1);
|
||||
|
||||
if (msg.intr) {
|
||||
if (numfds != 0)
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: too many fds for intr\n");
|
||||
pthread_kill(msg.thr, SIGUSR1);
|
||||
} else {
|
||||
if (numfds != 2)
|
||||
continue;
|
||||
|
||||
pthread_mutex_lock(&o.lock);
|
||||
process_message(&o, &msg, rfds[0], rfds[1]);
|
||||
pthread_mutex_unlock(&o.lock);
|
||||
}
|
||||
}
|
||||
if (o.fds)
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: open file descriptors on exit\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int nullfd;
|
||||
char *end;
|
||||
int cfd;
|
||||
sigset_t empty;
|
||||
|
||||
if (argc != 2 || !argv[1][0])
|
||||
goto out_inval;
|
||||
|
||||
cfd = strtol(argv[1], &end, 10);
|
||||
if (*end)
|
||||
goto out_inval;
|
||||
|
||||
/* demonize current process */
|
||||
switch(fork()) {
|
||||
case -1:
|
||||
perror("ulockmgr_server: fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (setsid() == -1) {
|
||||
perror("ulockmgr_server: setsid");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
(void) chdir("/");
|
||||
|
||||
sigemptyset(&empty);
|
||||
sigprocmask(SIG_SETMASK, &empty, NULL);
|
||||
|
||||
if (dup2(cfd, 4) == -1) {
|
||||
perror("ulockmgr_server: dup2");
|
||||
exit(1);
|
||||
}
|
||||
cfd = 4;
|
||||
nullfd = open("/dev/null", O_RDWR);
|
||||
if (nullfd >= 0) {
|
||||
dup2(nullfd, 0);
|
||||
dup2(nullfd, 1);
|
||||
}
|
||||
close(3);
|
||||
closefrom(5);
|
||||
while (1) {
|
||||
char c;
|
||||
int sock;
|
||||
int pid;
|
||||
int numfds = 1;
|
||||
int res = receive_message(cfd, &c, sizeof(c), &sock, &numfds);
|
||||
if (!res)
|
||||
break;
|
||||
if (res == -1)
|
||||
exit(1);
|
||||
assert(numfds == 1);
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("ulockmgr_server: fork");
|
||||
close(sock);
|
||||
continue;
|
||||
}
|
||||
if (pid == 0) {
|
||||
close(cfd);
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("ulockmgr_server: fork");
|
||||
_exit(1);
|
||||
}
|
||||
if (pid == 0)
|
||||
process_owner(sock);
|
||||
_exit(0);
|
||||
}
|
||||
waitpid(pid, NULL, 0);
|
||||
close(sock);
|
||||
}
|
||||
return 0;
|
||||
|
||||
out_inval:
|
||||
fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]);
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue
Block a user