* Implemented flock() semantics to the advisory locking backend. Not tested

(must also compare to BSD; I've looked at their sources, but I might have
  missed something).
* Added sys/file.h and the flock() system call.
* common_fcntl() could forget to put back the file descriptor on some error
  conditions (I guess we should introduce and use a DescriptorGetter class).
* Cleaned up fcntl.h, moved the BSD extensions S_IREAD and S_IWRITE to
  sys/stat.h where they belong, and added the missing S_IEXEC to them.
* Added some more comments.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23836 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-02-03 15:37:31 +00:00
parent e5bc2a9e7a
commit a32a4683ff
8 changed files with 162 additions and 36 deletions
headers
src/system
kernel/fs
libroot/posix/sys

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007, Haiku Inc. All Rights Reserved.
* Copyright 2002-2008, Haiku Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _FCNTL_H
@ -74,15 +74,6 @@ struct flock {
pid_t l_pid;
};
/* for use with flock() - TODO: this should be moved to sys/file.h *if* we'll support flock() one day */
#define LOCK_SH 0x01 /* shared file lock */
#define LOCK_EX 0x02 /* exclusive file lock */
#define LOCK_NB 0x04 /* don't block when locking */
#define LOCK_UN 0x08 /* unlock file */
#define S_IREAD 0x0100 /* owner may read */
#define S_IWRITE 0x0080 /* owner may write */
#ifdef __cplusplus
extern "C" {
@ -99,4 +90,4 @@ extern int fcntl(int fd, int op, ...);
}
#endif
#endif /* _FCNTL_H */
#endif /* _FCNTL_H */

29
headers/posix/sys/file.h Normal file

@ -0,0 +1,29 @@
/*
* Copyright 2008, Haiku Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _SYS_FILE_H
#define _SYS_FILE_H
#include <sys/types.h>
/* for use with flock() */
#define LOCK_SH 0x01 /* shared file lock */
#define LOCK_EX 0x02 /* exclusive file lock */
#define LOCK_NB 0x04 /* don't block when locking */
#define LOCK_UN 0x08 /* unlock file */
#ifdef __cplusplus
extern "C" {
#endif
extern int flock(int fd, int op);
#ifdef __cplusplus
}
#endif
#endif /* _SYS_FILE_H */

@ -1,5 +1,5 @@
/*
* Copyright 2002-2006, Haiku Inc. All Rights Reserved.
* Copyright 2002-2008, Haiku Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _SYS_STAT_H_
@ -85,6 +85,11 @@ struct stat {
#define S_IWOTH 00002 /* write permission: other */
#define S_IXOTH 00001 /* execute permission: other */
/* BSD extensions */
#define S_IREAD S_IRUSR
#define S_IWRITE S_IWUSR
#define S_IEXEC S_IXUSR
#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO)
#define ALLPERMS (S_ISUID | S_ISGID | S_ISTXT | S_IRWXU | S_IRWXG | S_IRWXO)
#define DEFFILEMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

@ -1,5 +1,5 @@
/*
* Copyright 2004-2007, Haiku Inc. All rights reserved.
* Copyright 2004-2008, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_SYSCALLS_H
@ -151,6 +151,7 @@ extern int _kern_open_parent_dir(int fd, char *name,
size_t nameLength);
extern status_t _kern_fcntl(int fd, int op, uint32 argument);
extern status_t _kern_fsync(int fd);
extern status_t _kern_flock(int fd, int op);
extern off_t _kern_seek(int fd, off_t pos, int seekType);
extern status_t _kern_create_dir_entry_ref(dev_t device, ino_t inode, const char *name, int perms);
extern status_t _kern_create_dir(int fd, const char *path, int perms);

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
@ -145,6 +145,7 @@ int _user_open_dir(int fd, const char *path);
int _user_open_parent_dir(int fd, char *name, size_t nameLength);
status_t _user_fcntl(int fd, int op, uint32 argument);
status_t _user_fsync(int fd);
status_t _user_flock(int fd, int op);
status_t _user_read_stat(int fd, const char *path, bool traverseLink,
struct stat *stat, size_t statSize);
status_t _user_write_stat(int fd, const char *path, bool traverseLink,

@ -15,6 +15,7 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/file.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
@ -128,6 +129,7 @@ struct advisory_locking {
struct advisory_lock {
list_link link;
team_id team;
pid_t session;
off_t offset;
off_t length;
bool shared;
@ -1088,7 +1090,6 @@ err1:
/*! Retrieves the first lock that has been set by the current team.
*/
static status_t
get_advisory_lock(struct vnode *vnode, struct flock *flock)
{
@ -1128,15 +1129,18 @@ release_advisory_lock(struct vnode *vnode, struct flock *flock)
return flock != NULL ? B_BAD_VALUE : B_OK;
team_id team = team_get_current_team_id();
pid_t session = thread_get_current_thread()->team->session_id;
// find matching lock entry
status_t status = B_BAD_VALUE;
struct advisory_lock *lock = NULL;
while ((lock = (struct advisory_lock *)list_get_next_item(&locking->locks, lock)) != NULL) {
if (lock->team == team && (flock == NULL || (flock != NULL
&& lock->offset == flock->l_start
&& lock->length == flock->l_len))) {
while ((lock = (struct advisory_lock *)list_get_next_item(&locking->locks,
lock)) != NULL) {
if (lock->team == team && (flock == NULL
|| (flock != NULL && lock->offset == flock->l_start
&& lock->length == flock->l_len))
|| lock->session == session) {
// we found our lock, free it
list_remove_item(&locking->locks, lock);
free(lock);
@ -1176,8 +1180,18 @@ release_advisory_lock(struct vnode *vnode, struct flock *flock)
}
/*! Acquires an advisory lock for the \a vnode. If \a wait is \c true, it
will wait for the lock to become available, if there are any collisions
(it will return B_PERMISSION_DENIED in this case if \a wait is \c false).
If \a session is -1, POSIX semantics are used for this lock. Otherwise,
BSD flock() semantics are used, that is, all children can unlock the file
in question (we even allow parents to remove the lock, though, but that
seems to be in line to what the BSD's are doing).
*/
static status_t
acquire_advisory_lock(struct vnode *vnode, struct flock *flock, bool wait)
acquire_advisory_lock(struct vnode *vnode, pid_t session, struct flock *flock,
bool wait)
{
FUNCTION(("acquire_advisory_lock(vnode = %p, flock = %p, wait = %s)\n",
vnode, flock, wait ? "yes" : "no"));
@ -1185,6 +1199,8 @@ acquire_advisory_lock(struct vnode *vnode, struct flock *flock, bool wait)
bool shared = flock->l_type == F_RDLCK;
status_t status = B_OK;
// TODO: do deadlock detection!
restart:
// if this vnode has an advisory_locking structure attached,
// lock that one and search for any colliding file lock
@ -1194,7 +1210,8 @@ restart:
if (locking != NULL) {
// test for collisions
struct advisory_lock *lock = NULL;
while ((lock = (struct advisory_lock *)list_get_next_item(&locking->locks, lock)) != NULL) {
while ((lock = (struct advisory_lock *)list_get_next_item(
&locking->locks, lock)) != NULL) {
if (lock->offset <= flock->l_start + flock->l_len
&& lock->offset + lock->length > flock->l_start) {
// locks do overlap
@ -1214,9 +1231,10 @@ restart:
if (waitForLock >= B_OK) {
if (!wait)
status = B_PERMISSION_DENIED;
status = session != -1 ? B_WOULD_BLOCK : B_PERMISSION_DENIED;
else {
status = switch_sem_etc(locking->lock, waitForLock, 1, B_CAN_INTERRUPT, 0);
status = switch_sem_etc(locking->lock, waitForLock, 1,
B_CAN_INTERRUPT, 0);
if (status == B_OK) {
// see if we're still colliding
goto restart;
@ -1240,7 +1258,8 @@ restart:
// we own the locking object, so it can't go away
}
struct advisory_lock *lock = (struct advisory_lock *)malloc(sizeof(struct advisory_lock));
struct advisory_lock *lock = (struct advisory_lock *)malloc(
sizeof(struct advisory_lock));
if (lock == NULL) {
if (waitForLock >= B_OK)
release_sem_etc(waitForLock, 1, B_RELEASE_ALL);
@ -1249,6 +1268,7 @@ restart:
}
lock->team = team_get_current_team_id();
lock->session = session;
// values must already be normalized when getting here
lock->offset = flock->l_start;
lock->length = flock->l_len;
@ -1261,6 +1281,10 @@ restart:
}
/*! Normalizes the \a flock structure to make it easier to compare the
structure with others. The l_start and l_len fields are set to absolute
values according to the l_whence field.
*/
static status_t
normalize_flock(struct file_descriptor *descriptor, struct flock *flock)
{
@ -1279,7 +1303,8 @@ normalize_flock(struct file_descriptor *descriptor, struct flock *flock)
if (FS_CALL(vnode, read_stat) == NULL)
return EOPNOTSUPP;
status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, &stat);
status = FS_CALL(vnode, read_stat)(vnode->mount->cookie,
vnode->private_node, &stat);
if (status < B_OK)
return status;
@ -4512,7 +4537,6 @@ common_fcntl(int fd, int op, uint32 argument, bool kernel)
struct file_descriptor *descriptor;
struct vnode *vnode;
struct flock flock;
status_t status;
FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n",
fd, op, argument, kernel ? "kernel" : "user"));
@ -4521,11 +4545,19 @@ common_fcntl(int fd, int op, uint32 argument, bool kernel)
if (descriptor == NULL)
return B_FILE_ERROR;
status_t status = B_OK;
if (op == F_SETLK || op == F_SETLKW || op == F_GETLK) {
if (descriptor->type != FDTYPE_FILE)
return B_BAD_VALUE;
if (user_memcpy(&flock, (struct flock *)argument, sizeof(struct flock)) < B_OK)
return B_BAD_ADDRESS;
status = B_BAD_VALUE;
else if (user_memcpy(&flock, (struct flock *)argument,
sizeof(struct flock)) < B_OK)
status = B_BAD_ADDRESS;
if (status != B_OK) {
put_fd(descriptor);
return status;
}
}
switch (op) {
@ -4564,8 +4596,8 @@ common_fcntl(int fd, int op, uint32 argument, bool kernel)
vnode->private_node, descriptor->cookie, (int)argument);
if (status == B_OK) {
// update this descriptor's open_mode field
descriptor->open_mode = (descriptor->open_mode & ~(O_APPEND | O_NONBLOCK))
| argument;
descriptor->open_mode = (descriptor->open_mode
& ~(O_APPEND | O_NONBLOCK)) | argument;
}
} else
status = EOPNOTSUPP;
@ -4595,7 +4627,8 @@ common_fcntl(int fd, int op, uint32 argument, bool kernel)
status = get_advisory_lock(descriptor->u.vnode, &flock);
if (status == B_OK) {
// copy back flock structure
status = user_memcpy((struct flock *)argument, &flock, sizeof(struct flock));
status = user_memcpy((struct flock *)argument, &flock,
sizeof(struct flock));
}
break;
@ -4609,11 +4642,15 @@ common_fcntl(int fd, int op, uint32 argument, bool kernel)
status = release_advisory_lock(descriptor->u.vnode, &flock);
else {
// the open mode must match the lock type
if ((descriptor->open_mode & O_RWMASK) == O_RDONLY && flock.l_type == F_WRLCK
|| (descriptor->open_mode & O_RWMASK) == O_WRONLY && flock.l_type == F_RDLCK)
if ((descriptor->open_mode & O_RWMASK) == O_RDONLY
&& flock.l_type == F_WRLCK
|| (descriptor->open_mode & O_RWMASK) == O_WRONLY
&& flock.l_type == F_RDLCK)
status = B_FILE_ERROR;
else
status = acquire_advisory_lock(descriptor->u.vnode, &flock, op == F_SETLKW);
else {
status = acquire_advisory_lock(descriptor->u.vnode, -1,
&flock, op == F_SETLKW);
}
}
break;
@ -7287,6 +7324,43 @@ _user_fsync(int fd)
}
status_t
_user_flock(int fd, int op)
{
struct file_descriptor *descriptor;
struct vnode *vnode;
struct flock flock;
status_t status;
FUNCTION(("_user_fcntl(fd = %d, op = %d)\n", fd, op));
descriptor = get_fd_and_vnode(fd, &vnode, false);
if (descriptor == NULL)
return B_FILE_ERROR;
if (descriptor->type != FDTYPE_FILE) {
put_fd(descriptor);
return B_BAD_VALUE;
}
flock.l_start = 0;
flock.l_len = OFF_MAX;
flock.l_whence = 0;
flock.l_type = (op & LOCK_SH) != 0 ? F_RDLCK : F_WRLCK;
if ((op & LOCK_UN) != 0)
status = release_advisory_lock(descriptor->u.vnode, &flock);
else {
status = acquire_advisory_lock(descriptor->u.vnode,
thread_get_current_thread()->team->session_id, &flock,
(op & LOCK_NB) == 0);
}
put_fd(descriptor);
return status;
}
status_t
_user_lock_node(int fd)
{

@ -4,6 +4,7 @@ UseHeaders $(TARGET_PRIVATE_KERNEL_HEADERS) : true ;
MergeObject posix_sys.o :
chmod.c
flock.c
ftime.c
getrusage.c
gettimeofday.c

@ -0,0 +1,24 @@
/*
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <syscalls.h>
#include <sys/file.h>
#include <errno.h>
int
flock(int fd, int op)
{
status_t status = _kern_flock(fd, op);
if (status < B_OK) {
errno = status;
return -1;
}
return 0;
}