|  | /**************************************************************************** | 
|  | * 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 <nuttx/config.h> | 
|  |  | 
|  | #include <fcntl.h> | 
|  | #include <errno.h> | 
|  | #include <search.h> | 
|  | #include <unistd.h> | 
|  | #include <sys/stat.h> | 
|  |  | 
|  | #include <nuttx/lib/lib.h> | 
|  | #include <nuttx/kmalloc.h> | 
|  | #include <nuttx/mutex.h> | 
|  | #include <nuttx/list.h> | 
|  |  | 
|  | #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_get_path | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static int file_lock_get_path(FAR struct file *filep, FAR char *path) | 
|  | { | 
|  | FAR struct tcb_s *tcb = nxsched_self(); | 
|  |  | 
|  | /* We only apply file lock on mount points (f_inode won't be NULL). */ | 
|  |  | 
|  | if (!INODE_IS_MOUNTPT(filep->f_inode) || | 
|  | tcb->flags & TCB_FLAG_SIGNAL_ACTION) | 
|  | { | 
|  | return -EBADF; | 
|  | } | 
|  |  | 
|  | return file_fcntl(filep, F_GETPATH, path); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * 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 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 (start < 0) | 
|  | { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (flock->l_len > 0) | 
|  | { | 
|  | if (flock->l_len - 1 > OFFSET_MAX - start) | 
|  | { | 
|  | return -EOVERFLOW; | 
|  | } | 
|  |  | 
|  | 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_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_lock_get_path(filep, 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; | 
|  | } | 
|  |  | 
|  | 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)); | 
|  | goto out; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | flock->l_type = F_UNLCK; | 
|  |  | 
|  | /* 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 | 
|  | */ | 
|  |  | 
|  | out: | 
|  | nxmutex_unlock(&g_protect_lock); | 
|  | 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_lock_get_path(filep, 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; | 
|  | } | 
|  |  | 
|  | request.l_pid = getpid(); | 
|  |  | 
|  | 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_lock_get_path(filep, 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); | 
|  | } |