diff --git a/fs/Kconfig b/fs/Kconfig index e66f2abcd5..6115ed19c7 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -64,6 +64,10 @@ config FS_NEPOLL_DESCRIPTORS ---help--- The maximum number of default epoll descriptors for epoll_create1(2) +config FS_LOCK_BUCKET_SIZE + int "Maximum number of hash bucket using file locks" + default 0 + config DISABLE_PSEUDOFS_OPERATIONS bool "Disable pseudo-filesystem operations" default DEFAULT_SMALL diff --git a/fs/fs_initialize.c b/fs/fs_initialize.c index df7678d988..c7285ee1ab 100644 --- a/fs/fs_initialize.c +++ b/fs/fs_initialize.c @@ -29,6 +29,7 @@ #include "rpmsgfs/rpmsgfs.h" #include "inode/inode.h" #include "aio/aio.h" +#include "vfs/lock.h" /**************************************************************************** * Private Functions @@ -83,6 +84,8 @@ void fs_initialize(void) inode_initialize(); + file_initlk(); + #ifdef CONFIG_FS_AIO /* Initialize for asynchronous I/O */ diff --git a/fs/vfs/Make.defs b/fs/vfs/Make.defs index de19051342..1e8c5b8d47 100644 --- a/fs/vfs/Make.defs +++ b/fs/vfs/Make.defs @@ -29,6 +29,10 @@ CSRCS += fs_syncfs.c fs_truncate.c # Certain interfaces are not available if there is no mountpoint support +ifneq ($(CONFIG_FS_LOCK_BUCKET_SIZE),0) +CSRCS += fs_lock.c +endif + ifneq ($(CONFIG_PSEUDOFS_SOFTLINKS),0) CSRCS += fs_link.c fs_symlink.c fs_readlink.c endif diff --git a/fs/vfs/fs_close.c b/fs/vfs/fs_close.c index 926f48f480..3015223853 100644 --- a/fs/vfs/fs_close.c +++ b/fs/vfs/fs_close.c @@ -32,6 +32,7 @@ #include #include "inode/inode.h" +#include "vfs/lock.h" /**************************************************************************** * Public Functions @@ -65,6 +66,8 @@ int file_close(FAR struct file *filep) if (inode) { + file_closelk(filep); + /* Close the file, driver, or mountpoint. */ if (inode->u.i_ops && inode->u.i_ops->close) diff --git a/fs/vfs/fs_fcntl.c b/fs/vfs/fs_fcntl.c index 6cf31a0536..925a605e89 100644 --- a/fs/vfs/fs_fcntl.c +++ b/fs/vfs/fs_fcntl.c @@ -35,6 +35,7 @@ #include #include "inode/inode.h" +#include "lock.h" /**************************************************************************** * Private Functions @@ -195,6 +196,12 @@ static int file_vfcntl(FAR struct file *filep, int cmd, va_list ap) * for the lock type which shall be set to F_UNLCK. */ + { + FAR struct flock *flock = va_arg(ap, FAR struct flock *); + ret = file_getlk(filep, flock); + } + + break; case F_SETLK: /* Set or clear a file segment lock according to the lock * description pointed to by the third argument, arg, taken as a @@ -206,6 +213,12 @@ static int file_vfcntl(FAR struct file *filep, int cmd, va_list ap) * shall return immediately with a return value of -1. */ + { + FAR struct flock *flock = va_arg(ap, FAR struct flock *); + ret = file_setlk(filep, flock, true); + } + + break; case F_SETLKW: /* This command shall be equivalent to F_SETLK except that if a * shared or exclusive lock is blocked by other locks, the thread @@ -216,9 +229,12 @@ static int file_vfcntl(FAR struct file *filep, int cmd, va_list ap) * the lock operation shall not be done. */ - ret = -ENOSYS; /* Not implemented */ - break; + { + FAR struct flock *flock = va_arg(ap, FAR struct flock *); + ret = file_setlk(filep, flock, false); + } + break; case F_GETPATH: /* Get the path of the file descriptor. The argument must be a buffer * of size PATH_MAX or greater. diff --git a/fs/vfs/fs_lock.c b/fs/vfs/fs_lock.c new file mode 100644 index 0000000000..7762fc258b --- /dev/null +++ b/fs/vfs/fs_lock.c @@ -0,0 +1,769 @@ +/**************************************************************************** + * fs/vfs/fs_lock.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "lock.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_FS_LARGEFILE +# define OFFSET_MAX INT64_MAX +#else +# define OFFSET_MAX INT32_MAX +#endif + +#define l_end l_len + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct file_lock_s +{ + struct flock fl_lock; /* File lock related information */ + FAR struct file *fl_file; /* Identifies the file descriptor information + * held by the caller + */ + struct list_node fl_node; /* Used to manage each filelock by means of a + * chained list. + */ +}; + +struct file_lock_bucket_s +{ + struct list_node list; /* Manage a chained list for each + * filelock + */ + sem_t wait; /* Blocking lock, called when SETLKW is + * called and there is a conflict. + */ + size_t nwaiter; /* Indicates how many blocking locks are + * currently blocked. + */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct hsearch_data g_file_lock_table; +static mutex_t g_protect_lock = NXMUTEX_INITIALIZER; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: file_lock_normalize + ****************************************************************************/ + +static int file_lock_normalize(FAR struct file *filep, + FAR struct flock *flock, + FAR struct flock *out) +{ + off_t start; + off_t end; + + /* Check the legality of incoming flocks */ + + if (flock->l_len - 1 > OFFSET_MAX - flock->l_start) + { + return -EOVERFLOW; + } + + /* Check that the type brought in the flock is correct */ + + switch (flock->l_type) + { + case F_RDLCK: + case F_WRLCK: + case F_UNLCK: + break; + default: + return -EINVAL; + } + + /* Converts and saves flock information */ + + switch (flock->l_whence) + { + case SEEK_SET: + { + start = 0; + } + + break; + case SEEK_CUR: + { + start = filep->f_pos; + } + + break; + case SEEK_END: + { + struct stat st; + int ret; + + ret = file_fstat(filep, &st); + if (ret < 0) + { + return ret; + } + + start = st.st_size; + } + + break; + default: + return -EINVAL; + } + + /* Check for overflow in converted flock */ + + if (flock->l_start > OFFSET_MAX - start) + { + return -EOVERFLOW; + } + + start += flock->l_start; + if (flock->l_len > 0) + { + end = start + flock->l_len - 1; + } + else if (flock->l_len < 0) + { + if (start + flock->l_len < 0) + { + return -EINVAL; + } + + end = start - 1; + start += flock->l_len; + } + else + { + end = OFFSET_MAX; + } + + out->l_whence = SEEK_SET; + out->l_pid = getpid(); + out->l_type = flock->l_type; + out->l_start = start; + out->l_end = end; + + return OK; +} + +/**************************************************************************** + * Name: file_lock_delete + ****************************************************************************/ + +static void file_lock_delete(FAR struct file_lock_s *file_lock) +{ + list_delete(&file_lock->fl_node); + kmm_free(file_lock); +} + +/**************************************************************************** + * Name: file_lock_delete_bucket + ****************************************************************************/ + +static void file_lock_delete_bucket(FAR struct file_lock_bucket_s *bucket, + FAR const char *filepath) +{ + ENTRY item; + + /* If there is still a lock on the chain table at this point, it means + * that there is still someone else holding it, so it doesn't need to be + * released + */ + + if (list_is_empty(&bucket->list)) + { + /* At this point, the file has no lock information context, so we can + * remove it from the hash table, and the return result is 0 or 1 means + * that the node does not exist, so we do not need to care about the + * final return results + */ + + item.key = (FAR char *)filepath; + hsearch_r(item, DELETE, NULL, &g_file_lock_table); + } +} + +/**************************************************************************** + * Name: file_lock_is_conflict + ****************************************************************************/ + +static bool file_lock_is_conflict(FAR struct flock *request, + FAR struct flock *internal) +{ + /* If the request is not exactly to the left or right of the internal, + * then there is an overlap. + */ + + if (request->l_start <= internal->l_end && request->l_end >= + internal->l_start) + { + if (request->l_type == F_WRLCK || internal->l_type == F_WRLCK) + { + return request->l_pid != internal->l_pid; + } + } + + return false; +} + +/**************************************************************************** + * Name: file_lock_find_bucket + ****************************************************************************/ + +static FAR struct file_lock_bucket_s * +file_lock_find_bucket(FAR const char *filepath) +{ + FAR ENTRY *hretvalue; + ENTRY item; + + item.key = (FAR char *)filepath; + item.data = NULL; + + if (hsearch_r(item, FIND, &hretvalue, &g_file_lock_table) == 1) + { + return hretvalue->data; + } + + return NULL; +} + +/**************************************************************************** + * Name: file_lock_create_bucket + ****************************************************************************/ + +static FAR struct file_lock_bucket_s * +file_lock_create_bucket(FAR const char *filepath) +{ + FAR struct file_lock_bucket_s *bucket; + FAR ENTRY *hretvalue; + ENTRY item; + + bucket = kmm_zalloc(sizeof(*bucket)); + if (bucket == NULL) + { + return NULL; + } + + /* Creating an instance store */ + + item.key = strdup(filepath); + if (item.key == NULL) + { + kmm_free(bucket); + return NULL; + } + + item.data = bucket; + + if (hsearch_r(item, ENTER, &hretvalue, &g_file_lock_table) == 0) + { + lib_free(item.key); + kmm_free(bucket); + return NULL; + } + + list_initialize(&bucket->list); + nxsem_init(&bucket->wait, 0, 0); + + return bucket; +} + +/**************************************************************************** + * Name: file_lock_modify + ****************************************************************************/ + +static int file_lock_modify(FAR struct file *filep, + FAR struct file_lock_bucket_s *bucket, + FAR struct flock *request) +{ + FAR struct file_lock_s *new_file_lock = NULL; + FAR struct file_lock_s *right = NULL; + FAR struct file_lock_s *left = NULL; + FAR struct file_lock_s *file_lock; + FAR struct file_lock_s *tmp; + bool added = false; + bool find = false; + + list_for_every_entry_safe(&bucket->list, file_lock, tmp, + struct file_lock_s, fl_node) + { + if (request->l_pid != file_lock->fl_lock.l_pid) + { + /* Only file locks with the same pid need to be processed, so the + * lookup is skipped. + */ + + if (find) + { + /* We've searched around and come back to the beginning. */ + + break; + } + } + else + { + find = true; + + /* Checking the type of overlapping locks */ + + if (request->l_type == file_lock->fl_lock.l_type) + { + /* Compare the starting point of the last lock with the + * starting point of the request, and use start - 1 instead of + * end + 1, because if end is "off_t" max, then end + 1 will + * be negative. + */ + + if (request->l_start - 1 > file_lock->fl_lock.l_end) + { + continue; + } + + if (request->l_end < file_lock->fl_lock.l_start - 1) + { + break; + } + + /* If the two locks are of the same type, then they are merged + * into one lock with a lower start position and a higher end + * position. + */ + + if (request->l_start < file_lock->fl_lock.l_start) + { + file_lock->fl_lock.l_start = request->l_start; + } + else + { + request->l_start = file_lock->fl_lock.l_start; + } + + if (request->l_end > file_lock->fl_lock.l_end) + { + file_lock->fl_lock.l_end = request->l_end; + } + else + { + request->l_end = file_lock->fl_lock.l_end; + } + + if (added) + { + file_lock_delete(file_lock); + continue; + } + + request = &file_lock->fl_lock; + added = true; + } + else + { + if (request->l_start > file_lock->fl_lock.l_end) + { + continue; + } + + if (request->l_end < file_lock->fl_lock.l_start) + { + break; + } + + /* Scenarios for handling different types of locks */ + + if (request->l_type == F_UNLCK) + { + added = true; + } + + /* The new lock and the old lock are adjacent or overlapping. + * The code will handle this depending on the situation. + * If the end address of the old lock is higher than the + * new lock, then go ahead and insert the new lock here. + */ + + if (request->l_start > file_lock->fl_lock.l_start) + { + left = file_lock; + } + + if (request->l_end < file_lock->fl_lock.l_end) + { + right = file_lock; + break; + } + + if (request->l_start <= file_lock->fl_lock.l_start) + { + /* In other cases, we are replacing old locks with new + * ones + */ + + if (added) + { + file_lock_delete(file_lock); + continue; + } + + memcpy(&file_lock->fl_lock, request, sizeof(struct flock)); + added = true; + } + } + } + } + + if (!added) + { + if (request->l_type == F_UNLCK) + { + return OK; + } + + /* insert a new lock */ + + new_file_lock = kmm_zalloc(sizeof(struct file_lock_s)); + if (new_file_lock == NULL) + { + return -ENOMEM; + } + + new_file_lock->fl_file = filep; + memcpy(&new_file_lock->fl_lock, request, sizeof(struct flock)); + list_add_before(&file_lock->fl_node, &new_file_lock->fl_node); + file_lock = new_file_lock; + } + + if (right) + { + if (left == right) + { + /* Splitting old locks */ + + new_file_lock = kmm_zalloc(sizeof(struct file_lock_s)); + if (new_file_lock == NULL) + { + return -ENOMEM; + } + + left = new_file_lock; + memcpy(left, right, sizeof(struct file_lock_s)); + list_add_before(&file_lock->fl_node, &left->fl_node); + } + + right->fl_lock.l_start = request->l_end + 1; + } + + if (left) + { + left->fl_lock.l_end = request->l_start - 1; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: file_getlk + * + * Description: + * Attempts to lock the region (not a real lock), and if there is a + * conflict then returns information about the conflicting locks + * + * Input Parameters: + * filep - File structure instance + * flock - Lock types to be converted + * + * Returned Value: + * The resulting 0 on success. A errno value is returned on any failure. + * + ****************************************************************************/ + +int file_getlk(FAR struct file *filep, FAR struct flock *flock) +{ + FAR struct file_lock_bucket_s *bucket; + FAR struct file_lock_s *file_lock; + char path[PATH_MAX]; + int ret; + + /* We need to get the unique identifier (Path) via filep */ + + ret = file_fcntl(filep, F_GETPATH, path); + if (ret < 0) + { + return ret; + } + + /* Convert a flock to a posix lock */ + + ret = file_lock_normalize(filep, flock, flock); + if (ret < 0) + { + return ret; + } + + flock->l_type = F_UNLCK; + + nxmutex_lock(&g_protect_lock); + + bucket = file_lock_find_bucket(path); + if (bucket != NULL) + { + list_for_every_entry(&bucket->list, file_lock, struct file_lock_s, + fl_node) + { + if (file_lock_is_conflict(flock, &file_lock->fl_lock)) + { + memcpy(flock, &file_lock->fl_lock, sizeof(*flock)); + break; + } + } + } + + nxmutex_unlock(&g_protect_lock); + + /* Convert back to flock + * The flock information saved in filelock is used as an offset + * to the relative position. And for upper level applications, + * l_len should be converted to cover the data quantity + */ + + if (flock->l_end == OFFSET_MAX) + { + flock->l_len = 0; + } + else + { + flock->l_len = flock->l_end - flock->l_start + 1; + } + + return OK; +} + +/**************************************************************************** + * Name: file_setlk + * + * Description: + * Actual execution of locking and unlocking behaviors + * + * Input Parameters: + * filep - File structure instance + * flock - Lock types to be converted + * nonblock - Waiting for lock + * + * Returned Value: + * The resulting 0 on success. A errno value is returned on any failure. + * + ****************************************************************************/ + +int file_setlk(FAR struct file *filep, FAR struct flock *flock, + bool nonblock) +{ + FAR struct file_lock_bucket_s *bucket; + FAR struct file_lock_s *file_lock; + struct flock request; + char path[PATH_MAX]; + int ret; + + /* We need to get the unique identifier (Path) via filep */ + + ret = file_fcntl(filep, F_GETPATH, path); + if (ret < 0) + { + return ret; + } + + /* Convert a flock to a posix lock */ + + ret = file_lock_normalize(filep, flock, &request); + if (ret < 0) + { + return ret; + } + + nxmutex_lock(&g_protect_lock); + + bucket = file_lock_find_bucket(path); + if (bucket == NULL) + { + /* If we request to unlock and the bucket is not found, it means + * there is no lock here. + */ + + if (request.l_type == F_UNLCK) + { + nxmutex_unlock(&g_protect_lock); + return OK; + } + + /* It looks like we didn't find a bucket, let's go create one */ + + bucket = file_lock_create_bucket(path); + if (bucket == NULL) + { + nxmutex_unlock(&g_protect_lock); + return -ENOMEM; + } + } + else if (request.l_type != F_UNLCK) + { +retry: + list_for_every_entry(&bucket->list, file_lock, struct file_lock_s, + fl_node) + { + if (file_lock_is_conflict(&request, &file_lock->fl_lock)) + { + if (nonblock) + { + ret = -EAGAIN; + goto out; + } + + bucket->nwaiter++; + nxmutex_unlock(&g_protect_lock); + nxsem_wait(&bucket->wait); + nxmutex_lock(&g_protect_lock); + bucket->nwaiter--; + goto retry; + } + } + } + + ret = file_lock_modify(filep, bucket, &request); + if (ret < 0) + { + goto out; + } + + /* When there is a lock change, we need to wake up the blocking lock */ + + if (bucket->nwaiter > 0) + { + nxsem_post(&bucket->wait); + } + +out: + file_lock_delete_bucket(bucket, path); + nxmutex_unlock(&g_protect_lock); + return ret; +} + +/**************************************************************************** + * Name: file_closelk + * + * Description: + * Remove all locks associated with the filep when call close is applied. + * + * Input Parameters: + * filep - The filep that corresponds to the shutdown. + * + ****************************************************************************/ + +void file_closelk(FAR struct file *filep) +{ + FAR struct file_lock_bucket_s *bucket; + FAR struct file_lock_s *file_lock; + FAR struct file_lock_s *temp; + char path[PATH_MAX]; + bool deleted = false; + int ret; + + ret = file_fcntl(filep, F_GETPATH, path); + if (ret < 0) + { + /* It isn't an error if fs doesn't support F_GETPATH, so we just end + * it. + */ + + return; + } + + bucket = file_lock_find_bucket(path); + if (bucket == NULL) + { + /* There is no bucket here, so we don't need to free it. */ + + return; + } + + nxmutex_lock(&g_protect_lock); + list_for_every_entry_safe(&bucket->list, file_lock, temp, + struct file_lock_s, fl_node) + { + if (file_lock->fl_file == filep) + { + deleted = true; + file_lock_delete(file_lock); + } + } + + if (bucket->nwaiter > 0 && deleted) + { + nxsem_post(&bucket->wait); + } + else if (deleted) + { + file_lock_delete_bucket(bucket, path); + } + + nxmutex_unlock(&g_protect_lock); +} + +/**************************************************************************** + * Name: file_initlk + * + * Description: + * Initializing file locks + * + ****************************************************************************/ + +void file_initlk(void) +{ + /* Initialize file lock context hash table */ + + hcreate_r(CONFIG_FS_LOCK_BUCKET_SIZE, &g_file_lock_table); +} diff --git a/fs/vfs/lock.h b/fs/vfs/lock.h new file mode 100644 index 0000000000..c1f888d22d --- /dev/null +++ b/fs/vfs/lock.h @@ -0,0 +1,106 @@ +/**************************************************************************** + * fs/vfs/lock.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __FS_VFS_LOCK_H +#define __FS_VFS_LOCK_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if CONFIG_FS_LOCK_BUCKET_SIZE == 0 +# define file_initlk() +# define file_closelk(filep) +# define file_getlk(filep, flock) ((void)flock, -ENOSYS) +# define file_setlk(filep, flock, nonblock) ((void)flock, -ENOSYS) +#else + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: file_initlk + * + * Description: + * Initializing file locks + * + ****************************************************************************/ + +void file_initlk(void); + +/**************************************************************************** + * Name: file_closelk + * + * Description: + * Remove all locks associated with the filep when call close is applied. + * + * Input Parameters: + * filep - The filep that corresponds to the shutdown. + * + ****************************************************************************/ + +void file_closelk(FAR struct file *filep); + +/**************************************************************************** + * Name: file_getlk + * + * Description: + * Attempts to lock the region (not a real lock), and if there is a + * conflict then returns information about the conflicting locks + * + * Input Parameters: + * filep - File structure instance + * flock - Lock types to be converted + * + * Returned Value: + * The resulting 0 on success. A errno value is returned on any failure. + * + ****************************************************************************/ + +int file_getlk(FAR struct file *filep, FAR struct flock *flock); + +/**************************************************************************** + * Name: file_setlk + * + * Description: + * Actual execution of locking and unlocking behaviors + * + * Input Parameters: + * filep - File structure instance + * flock - Lock types to be converted + * nonblock - Waiting for lock + * + * Returned Value: + * The resulting 0 on success. A errno value is returned on any failure. + * + ****************************************************************************/ + +int file_setlk(FAR struct file *filep, FAR struct flock *flock, + bool nonblock); + +#endif /* CONFIG_FS_LOCK_BUCKET_SIZE */ +#endif /* __FS_VFS_LOCK_H */ diff --git a/include/unistd.h b/include/unistd.h index cc88c84539..2e2f52419a 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -289,6 +289,18 @@ # define execvpe execve #endif +/* Commands for lockf() + * F_ULOCK - Unlock + * F_LOCK - Blocking Exclusive Lock + * F_TLOCK - Attempted Exclusive Locking + * F_TEST - Test Locked Status + */ + +#define F_ULOCK 0 +#define F_LOCK 1 +#define F_TLOCK 2 +#define F_TEST 3 + /**************************************************************************** * Public Data ****************************************************************************/ @@ -337,6 +349,7 @@ ssize_t pread(int fd, FAR void *buf, size_t nbytes, off_t offset); ssize_t pwrite(int fd, FAR const void *buf, size_t nbytes, off_t offset); int ftruncate(int fd, off_t length); int fchown(int fd, uid_t owner, gid_t group); +int lockf(int fd, int cmd, off_t len); /* Check if a file descriptor corresponds to a terminal I/O file */ diff --git a/libs/libc/unistd/Make.defs b/libs/libc/unistd/Make.defs index c30bc721be..6605e2fdbd 100644 --- a/libs/libc/unistd/Make.defs +++ b/libs/libc/unistd/Make.defs @@ -30,6 +30,7 @@ CSRCS += lib_setrlimit.c lib_getrlimit.c lib_setpriority.c lib_getpriority.c CSRCS += lib_futimes.c lib_lutimes.c lib_gethostname.c lib_sethostname.c CSRCS += lib_fchownat.c lib_linkat.c lib_readlinkat.c lib_symlinkat.c CSRCS += lib_unlinkat.c lib_usleep.c lib_getpgrp.c lib_getpgid.c +CSRCS += lib_lockf.c ifneq ($(CONFIG_SCHED_USER_IDENTITY),y) CSRCS += lib_setuid.c lib_setgid.c lib_getuid.c lib_getgid.c diff --git a/libs/libc/unistd/lib_lockf.c b/libs/libc/unistd/lib_lockf.c new file mode 100644 index 0000000000..4ceade3cc7 --- /dev/null +++ b/libs/libc/unistd/lib_lockf.c @@ -0,0 +1,117 @@ +/**************************************************************************** + * libs/libc/unistd/lib_lockf.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lockf + * + * Description: + * lockf() is a function that allows a process to apply or remove an + * advisory lock on an open file. The lock can be either a shared (read) + * lock or an exclusive (write) lock. This lock is advisory, meaning that + * it is not enforced by the system and it is up to cooperating processes + * to honor the lock. + * + * Input Parameters: + * fd - File descriptor of the open file. + * cmd - Specifies the type of lock operation to be performed. + * It can be one of the following: + * F_LOCK: Request an exclusive (write) lock. If the lock is not + * available, the call may block until it can be acquired. + * F_TLOCK: Try to request an exclusive (write) lock. If the lock is not + * available, the call will not block and will return + * immediately. + * F_ULOCK: Unlock an existing lock. + * F_TEST: Test the lock. Returns 0 if the file is unlocked or locked by + * the calling process, or -1 with errno set to EACCES if another + * process holds the lock. + * len - Length of the locked region, relative to the current file + * position. + * + * Returned Value: + * The returned value of lockf() depends on the success or failure of the + * operation. On success, the return value is 0. On failure, the return + * value is -1, and the errno variable is set to indicate the specific + * error. + * + ****************************************************************************/ + +int lockf(int fd, int cmd, off_t len) +{ + struct flock lock; + + lock.l_whence = SEEK_CUR; + lock.l_start = 0; + lock.l_len = len; + + switch (cmd) + { + case F_LOCK: + { + lock.l_type = F_WRLCK; + return fcntl(fd, F_SETLKW, &lock); + } + + case F_TLOCK: + { + lock.l_type = F_WRLCK; + return fcntl(fd, F_SETLK, &lock); + } + + case F_ULOCK: + { + lock.l_type = F_UNLCK; + return fcntl(fd, F_SETLK, &lock); + } + + case F_TEST: + { + lock.l_type = F_RDLCK; + if (fcntl(fd, F_GETLK, &lock) < 0) + { + return ERROR; + } + + /* Check result */ + + if (lock.l_type == F_UNLCK || lock.l_pid == getpid()) + { + return OK; + } + + set_errno(EACCES); + return ERROR; + } + } + + set_errno(EINVAL); + return ERROR; +}