From 9616f5d52532708692b3a9f12d808ed0ba91d539 Mon Sep 17 00:00:00 2001 From: Andrew Chambers Date: Wed, 15 Jun 2022 20:45:07 +1200 Subject: [PATCH] Implement locking with OFD locks when present This seems preferrable on linux because it lets diod support lock ranges in some form. Note this may affect applications that mix file access across diod mounts and regular mounts. --- configure.ac | 2 + diod/ioctx.c | 20 +++++++- diod/ioctx.h | 7 ++- diod/ops.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 160 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 419056e7..35d41947 100644 --- a/configure.ac +++ b/configure.ac @@ -75,6 +75,8 @@ AC_CHECK_HEADERS( \ AC_C_BIGENDIAN AC_C_CONST +AC_CHECK_DECLS([F_OFD_SETLK], [], [], [[#include ]]) + ## # Checks for library functions ## diff --git a/diod/ioctx.c b/diod/ioctx.c index c1265e4d..060e5e44 100644 --- a/diod/ioctx.c +++ b/diod/ioctx.c @@ -53,7 +53,9 @@ struct ioctx_struct { int refcount; int fd; DIR *dir; + #if !HAVE_DECL_F_OFD_SETLK int lock_type; + #endif Npqid qid; u32 iounit; u32 open_flags; @@ -165,7 +167,9 @@ _ioctx_create_open (Npuser *user, Path path, int flags, u32 mode) } pthread_mutex_init (&ioctx->lock, NULL); ioctx->refcount = 1; + #if !HAVE_DECL_F_OFD_SETLK ioctx->lock_type = LOCK_UN; + #endif ioctx->dir = NULL; ioctx->open_flags = flags; ioctx->user = user; @@ -353,6 +357,18 @@ ioctx_fsync(IOCtx ioctx) return fsync (ioctx->fd); } +#if HAVE_DECL_F_OFD_SETLK + +int +ioctx_fcntl_lock(IOCtx ioctx, int cmd, struct flock *l) +{ + if (fcntl(ioctx->fd, cmd, l) < 0) + return -1; + return 0; +} + +#else + int ioctx_flock (IOCtx ioctx, int operation) { @@ -370,7 +386,7 @@ ioctx_flock (IOCtx ioctx, int operation) /* If lock of 'type' could be obtained, return LOCK_UN, otherwise LOCK_EX. */ int -ioctx_testlock (IOCtx ioctx, int type) +ioctx_test_flock (IOCtx ioctx, int type) { int ret = LOCK_UN; @@ -407,6 +423,8 @@ ioctx_testlock (IOCtx ioctx, int type) return ret; } +#endif + u32 ioctx_iounit (IOCtx ioctx) { diff --git a/diod/ioctx.h b/diod/ioctx.h index 0b8a24b6..515257ed 100644 --- a/diod/ioctx.h +++ b/diod/ioctx.h @@ -28,9 +28,12 @@ struct dirent *ioctx_readdir(IOCtx ioctx, long *new_offset); void ioctx_rewinddir (IOCtx ioctx); void ioctx_seekdir (IOCtx ioctx, long offset); int ioctx_fsync (IOCtx ioctx); +#if HAVE_DECL_F_OFD_SETLK +int ioctx_fcntl_lock(IOCtx ioctx, int cmd, struct flock *l); +#else int ioctx_flock (IOCtx ioctx, int operation); -int ioctx_testlock (IOCtx ioctx, int operation); - +int ioctx_test_flock (IOCtx ioctx, int operation); +#endif int ioctx_stat (IOCtx ioctx, struct stat *sb); int ioctx_chmod (IOCtx ioctx, u32 mode); int ioctx_chown (IOCtx ioctx, u32 uid, u32 gid); diff --git a/diod/ops.c b/diod/ops.c index 77f960d4..13c62a7d 100644 --- a/diod/ops.c +++ b/diod/ops.c @@ -1253,6 +1253,137 @@ diod_fsync (Npfid *fid) return NULL; } +#if HAVE_DECL_F_OFD_SETLK + +/* Locking note: + * Implement POSIX locks in terms of OFD locks, it will + * at least work for some use cases, but may still deadlock others. + */ + +Npfcall* +diod_lock (Npfid *fid, u8 type, u32 flags, u64 start, u64 length, u32 proc_id, + Npstr *client_id) +{ + Fid *f = fid->aux; + Npfcall *ret; + u8 status = P9_LOCK_ERROR; + struct flock lock; + + if (flags & ~P9_LOCK_FLAGS_BLOCK) { /* only one valid flag for now */ + np_uerror (EINVAL); /* (which we ignore) */ + goto error; + } + if (!f->ioctx) { + msg ("diod_lock: fid is not open"); + np_uerror (EBADF); + goto error; + } + + lock.l_whence = SEEK_SET; + lock.l_start = start; + lock.l_len = length; + lock.l_pid = 0; + + switch (type) { + case P9_LOCK_TYPE_UNLCK: + lock.l_type = F_UNLCK; + if (ioctx_fcntl_lock(f->ioctx, F_OFD_SETLK, &lock) == 0) + status = P9_LOCK_SUCCESS; + break; + case P9_LOCK_TYPE_RDLCK: + lock.l_type = F_RDLCK; + if (ioctx_fcntl_lock(f->ioctx, F_OFD_SETLK, &lock) == 0) + status = P9_LOCK_SUCCESS; + else if (errno == EWOULDBLOCK) + status = P9_LOCK_BLOCKED; + break; + case P9_LOCK_TYPE_WRLCK: + lock.l_type = F_WRLCK; + if (ioctx_fcntl_lock(f->ioctx, F_OFD_SETLK, &lock) == 0) + status = P9_LOCK_SUCCESS; + else if (errno == EWOULDBLOCK) + status = P9_LOCK_BLOCKED; + break; + default: + np_uerror (EINVAL); + goto error; + } + if (!((ret = np_create_rlock (status)))) { + np_uerror (ENOMEM); + goto error; + } + return ret; +error: + errn (np_rerror (), "diod_lock %s@%s:%s", + fid->user->uname, np_conn_get_client_id (fid->conn), + path_s (f->path)); + return NULL; +} + +Npfcall* +diod_getlock (Npfid *fid, u8 type, u64 start, u64 length, u32 proc_id, + Npstr *client_id) +{ + Fid *f = fid->aux; + Npfcall *ret; + char *cid = NULL; + struct flock lock; + + if (!f->ioctx) { + msg ("diod_getlock: fid is not open"); + np_uerror (EBADF); + goto error; + } + if (!(cid = np_strdup (client_id))) { + np_uerror (ENOMEM); + goto error; + } + if (type != P9_LOCK_TYPE_RDLCK && type != P9_LOCK_TYPE_WRLCK) { + np_uerror (EINVAL); + goto error; + } + + lock.l_whence = SEEK_SET; + lock.l_start = start; + lock.l_len = length; + lock.l_pid = 0; + + switch (type) { + case P9_LOCK_TYPE_UNLCK: + lock.l_type = F_UNLCK; + case P9_LOCK_TYPE_RDLCK: + lock.l_type = F_RDLCK; + case P9_LOCK_TYPE_WRLCK: + lock.l_type = F_WRLCK; + default: + np_uerror (EINVAL); + goto error; + } + + if (ioctx_fcntl_lock(f->ioctx, F_OFD_GETLK, &lock) != 0) { + np_uerror (errno); + goto error; + } + + type = (lock.l_type == F_UNLCK) ? P9_LOCK_TYPE_UNLCK : type; + if (!((ret = np_create_rgetlock(type, start, length, lock.l_pid, cid)))) { + np_uerror (ENOMEM); + goto error; + } + free (cid); + return ret; +error: + errn (np_rerror (), "diod_getlock %s@%s:%s", + fid->user->uname, np_conn_get_client_id (fid->conn), + path_s (f->path)); + if (cid) + free (cid); + return NULL; +} + +#else + + /* Locking note: * Implement POSIX locks in terms of BSD flock locks. * This at least gets distributed whole-file locking to work. @@ -1331,7 +1462,7 @@ diod_getlock (Npfid *fid, u8 type, u64 start, u64 length, u32 proc_id, goto error; } ftype = (type == P9_LOCK_TYPE_RDLCK) ? LOCK_SH : LOCK_EX; - ftype = ioctx_testlock (f->ioctx, ftype); + ftype = ioctx_test_flock (f->ioctx, ftype); type = (ftype == LOCK_EX) ? P9_LOCK_TYPE_WRLCK : P9_LOCK_TYPE_UNLCK; if (!((ret = np_create_rgetlock(type, start, length, proc_id, cid)))) { np_uerror (ENOMEM); @@ -1348,6 +1479,8 @@ diod_getlock (Npfid *fid, u8 type, u64 start, u64 length, u32 proc_id, return NULL; } +#endif + Npfcall* diod_link (Npfid *dfid, Npfid *fid, Npstr *name) {