42388f16e9
apps/examples/usrsocktest/usrsocktest_basic_daemon.c: 321 static void basic_daemon_dup2(FAR struct usrsocktest_daemon_conf_s *dconf) 322 { ... 335 ret = dup2(sd2, sd); 352 } Usrsocktest Task hold the file group lock and send the close request to usrsock deamon : | #0 net_lockedwait_uninterruptible (sem=0x5555555f8ba2 <g_usrsockdev+34>) at utils/net_lock.c:427 | #1 0x000055555557489c in usrsockdev_do_request (conn=0x5555555f8800 <g_usrsock_connections>, iov=0x7ffff3f36040, iovcnt=1) at usrsock/usrsock_dev.c:1185 | --> send close request to usrsock deamon | | #2 0x00005555555d0439 in do_close_request (conn=0x5555555f8800 <g_usrsock_connections>) at usrsock/usrsock_close.c:109 | #3 0x00005555555d04f5 in usrsock_close (conn=0x5555555f8800 <g_usrsock_connections>) at usrsock/usrsock_close.c:157 | #4 0x00005555555cf100 in usrsock_sockif_close (psock=0x7ffff3ea4a60) at usrsock/usrsock_sockif.c:234 | #5 0x00005555555c7b2f in psock_close (psock=0x7ffff3ea4a60) at socket/net_close.c:102 | #6 0x000055555557a518 in sock_file_close (filep=0x7ffff3f253d0) at socket/socket.c:115 | #7 0x000055555557678f in file_close (filep=0x7ffff3f253d0) at vfs/fs_close.c:74 | #8 0x000055555557694c in file_dup2 (filep1=0x7ffff3f253e8, filep2=0x7ffff3f253d0) at vfs/fs_dup2.c:129 | ---> hold group file list lock ( _files_semtake(list) ) | | #9 0x0000555555575aab in nx_dup2 (fd1=7, fd2=6) at inode/fs_files.c:451 | #10 0x0000555555575af3 in dup2 (fd1=7, fd2=6) at inode/fs_files.c:473 | #11 0x000055555559d937 in basic_daemon_dup2 (dconf=0x5555555f8d80 <usrsocktest_daemon_config>) at usrsocktest_basic_daemon.c:335 | #12 0x000055555559ed80 in usrsocktest_test_basic_daemon_basic_daemon_dup2 () at usrsocktest_basic_daemon.c:612 | #13 0x000055555559f18d in usrsocktest_group_basic_daemon_run () at usrsocktest_basic_daemon.c:666 | #14 0x0000555555599f8d in run_tests (name=0x5555555dc8c3 "basic_daemon", test_fn=0x55555559ef50 <usrsocktest_group_basic_daemon_run>) at usrsocktest_main.c:117 | #15 0x000055555559a06c in run_all_tests () at usrsocktest_main.c:154 | #16 0x000055555559a3d1 in usrsocktest_main (argc=1, argv=0x7ffff3f25450) at usrsocktest_main.c:248 | #17 0x000055555555cad8 in nxtask_startup (entrypt=0x55555559a357 <usrsocktest_main>, argc=1, argv=0x7ffff3f25450) at sched/task_startup.c:70 | #18 0x0000555555559938 in nxtask_start () at task/task_start.c:134 Usrsock Deamon weakup and setup the poll want to perform close request, but locked on fs_getfilep(): | #0 _files_semtake (list=0x7ffff3f250b8) at inode/fs_files.c:51 | --> Request group lock but which hold by close request, deadlock | #1 0x00005555555758b1 in fs_getfilep (fd=5, filep=0x7ffff3f47190) at inode/fs_files.c:375 | #2 0x00005555555d3064 in poll_fdsetup (fd=5, fds=0x7ffff3f47290, setup=true) at vfs/fs_poll.c:79 | #3 0x00005555555d3243 in poll_setup (fds=0x7ffff3f47290, nfds=2, sem=0x7ffff3f47206) at vfs/fs_poll.c:139 | #4 0x00005555555d39a6 in nx_poll (fds=0x7ffff3f47290, nfds=2, timeout=-1) at vfs/fs_poll.c:383 | #5 0x00005555555d3abd in poll (fds=0x7ffff3f47290, nfds=2, timeout=-1) at vfs/fs_poll.c:501 | --> daemon weak up | #6 0x00005555555c62c7 in usrsocktest_daemon (param=0x5555555f5360 <g_ub_daemon>) at usrsocktest_daemon.c:1846 | #7 0x000055555559161e in pthread_startup (entry=0x5555555c60d3 <usrsocktest_daemon>, arg=0x5555555f5360 <g_ub_daemon>) at pthread/pthread_create.c:59 | #8 0x00005555555d45f0 in pthread_start () at pthread/pthread_create.c:175 | #9 0x0000000000000000 in ?? () Signed-off-by: chao an <anchao@xiaomi.com>
601 lines
15 KiB
C
601 lines
15 KiB
C
/****************************************************************************
|
|
* fs/inode/fs_files.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 <sys/types.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <sched.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/cancelpt.h>
|
|
#include <nuttx/semaphore.h>
|
|
|
|
#include "inode/inode.h"
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: _files_semtake
|
|
****************************************************************************/
|
|
|
|
static int _files_semtake(FAR struct filelist *list)
|
|
{
|
|
return nxsem_wait_uninterruptible(&list->fl_sem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: _files_semgive
|
|
****************************************************************************/
|
|
|
|
#define _files_semgive(list) nxsem_post(&list->fl_sem)
|
|
|
|
/****************************************************************************
|
|
* Name: files_extend
|
|
****************************************************************************/
|
|
|
|
static int files_extend(FAR struct filelist *list, size_t row)
|
|
{
|
|
FAR struct file **tmp;
|
|
int i;
|
|
|
|
if (row <= list->fl_rows)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (row * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK > OPEN_MAX)
|
|
{
|
|
return -EMFILE;
|
|
}
|
|
|
|
tmp = kmm_realloc(list->fl_files, sizeof(FAR struct file *) * row);
|
|
DEBUGASSERT(tmp);
|
|
if (tmp == NULL)
|
|
{
|
|
return -ENFILE;
|
|
}
|
|
|
|
i = list->fl_rows;
|
|
do
|
|
{
|
|
tmp[i] = kmm_zalloc(sizeof(struct file) *
|
|
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK);
|
|
if (tmp[i] == NULL)
|
|
{
|
|
while (--i >= list->fl_rows)
|
|
{
|
|
kmm_free(tmp[i]);
|
|
}
|
|
|
|
kmm_free(tmp);
|
|
return -ENFILE;
|
|
}
|
|
}
|
|
while (++i < row);
|
|
|
|
list->fl_files = tmp;
|
|
list->fl_rows = row;
|
|
|
|
/* Note: If assertion occurs, the fl_rows has a overflow.
|
|
* And there may be file descriptors leak in system.
|
|
*/
|
|
|
|
DEBUGASSERT(list->fl_rows == row);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: files_initlist
|
|
*
|
|
* Description: Initializes the list of files for a new task
|
|
*
|
|
****************************************************************************/
|
|
|
|
void files_initlist(FAR struct filelist *list)
|
|
{
|
|
DEBUGASSERT(list);
|
|
|
|
/* Initialize the list access mutex */
|
|
|
|
nxsem_init(&list->fl_sem, 0, 1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: files_releaselist
|
|
*
|
|
* Description:
|
|
* Release a reference to the file list
|
|
*
|
|
****************************************************************************/
|
|
|
|
void files_releaselist(FAR struct filelist *list)
|
|
{
|
|
int i;
|
|
int j;
|
|
|
|
DEBUGASSERT(list);
|
|
|
|
/* Close each file descriptor .. Normally, you would need take the list
|
|
* semaphore, but it is safe to ignore the semaphore in this context
|
|
* because there should not be any references in this context.
|
|
*/
|
|
|
|
for (i = list->fl_rows - 1; i >= 0; i--)
|
|
{
|
|
for (j = CONFIG_NFILE_DESCRIPTORS_PER_BLOCK - 1; j >= 0; j--)
|
|
{
|
|
file_close(&list->fl_files[i][j]);
|
|
}
|
|
|
|
kmm_free(list->fl_files[i]);
|
|
}
|
|
|
|
kmm_free(list->fl_files);
|
|
|
|
/* Destroy the semaphore */
|
|
|
|
nxsem_destroy(&list->fl_sem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: files_allocate
|
|
*
|
|
* Description:
|
|
* Allocate a struct files instance and associate it with an inode
|
|
* instance. Returns the file descriptor == index into the files array.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int files_allocate(FAR struct inode *inode, int oflags, off_t pos,
|
|
FAR void *priv, int minfd)
|
|
{
|
|
FAR struct filelist *list;
|
|
int ret;
|
|
int i;
|
|
int j;
|
|
|
|
/* Get the file descriptor list. It should not be NULL in this context. */
|
|
|
|
list = nxsched_get_files();
|
|
DEBUGASSERT(list != NULL);
|
|
|
|
ret = _files_semtake(list);
|
|
if (ret < 0)
|
|
{
|
|
/* Probably canceled */
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Calculate minfd whether is in list->fl_files.
|
|
* if not, allocate a new filechunk.
|
|
*/
|
|
|
|
i = minfd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
|
|
if (i >= list->fl_rows)
|
|
{
|
|
ret = files_extend(list, i + 1);
|
|
if (ret < 0)
|
|
{
|
|
_files_semgive(list);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Find free file */
|
|
|
|
j = minfd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
|
|
do
|
|
{
|
|
do
|
|
{
|
|
if (!list->fl_files[i][j].f_inode)
|
|
{
|
|
list->fl_files[i][j].f_oflags = oflags;
|
|
list->fl_files[i][j].f_pos = pos;
|
|
list->fl_files[i][j].f_inode = inode;
|
|
list->fl_files[i][j].f_priv = priv;
|
|
_files_semgive(list);
|
|
return i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j;
|
|
}
|
|
}
|
|
while (++j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK);
|
|
|
|
j = 0;
|
|
}
|
|
while (++i < list->fl_rows);
|
|
|
|
/* The space of file array isn't enough, allocate a new filechunk */
|
|
|
|
ret = files_extend(list, i + 1);
|
|
if (ret >= 0)
|
|
{
|
|
list->fl_files[i][0].f_oflags = oflags;
|
|
list->fl_files[i][0].f_pos = pos;
|
|
list->fl_files[i][0].f_inode = inode;
|
|
list->fl_files[i][0].f_priv = priv;
|
|
ret = i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK;
|
|
}
|
|
|
|
_files_semgive(list);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: files_duplist
|
|
*
|
|
* Description:
|
|
* Duplicate parent task's file descriptors.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int files_duplist(FAR struct filelist *plist, FAR struct filelist *clist)
|
|
{
|
|
int ret;
|
|
int i;
|
|
int j;
|
|
|
|
ret = _files_semtake(plist);
|
|
if (ret < 0)
|
|
{
|
|
/* Probably canceled */
|
|
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < plist->fl_rows; i++)
|
|
{
|
|
for (j = 0; j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; j++)
|
|
{
|
|
FAR struct file *filep;
|
|
#ifdef CONFIG_FDCLONE_STDIO
|
|
|
|
/* Determine how many file descriptors to clone. If
|
|
* CONFIG_FDCLONE_DISABLE is set, no file descriptors will be
|
|
* cloned. If CONFIG_FDCLONE_STDIO is set, only the first
|
|
* three descriptors (stdin, stdout, and stderr) will be
|
|
* cloned. Otherwise all file descriptors will be cloned.
|
|
*/
|
|
|
|
if (i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j >= 3)
|
|
{
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
filep = &plist->fl_files[i][j];
|
|
if (filep->f_inode == NULL || (filep->f_oflags & O_CLOEXEC) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ret = files_extend(clist, i + 1);
|
|
if (ret < 0)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
/* Yes... duplicate it for the child */
|
|
|
|
ret = file_dup2(filep, &clist->fl_files[i][j]);
|
|
if (ret < 0)
|
|
{
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
_files_semgive(plist);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: fs_getfilep
|
|
*
|
|
* Description:
|
|
* Given a file descriptor, return the corresponding instance of struct
|
|
* file.
|
|
*
|
|
* Input Parameters:
|
|
* fd - The file descriptor
|
|
* filep - The location to return the struct file instance
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) is returned on success; a negated errno value is returned on
|
|
* any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int fs_getfilep(int fd, FAR struct file **filep)
|
|
{
|
|
FAR struct filelist *list;
|
|
int ret;
|
|
|
|
DEBUGASSERT(filep != NULL);
|
|
*filep = (FAR struct file *)NULL;
|
|
|
|
list = nxsched_get_files();
|
|
|
|
/* The file list can be NULL under two cases: (1) One is an obscure
|
|
* cornercase: When memory management debug output is enabled. Then
|
|
* there may be attempts to write to stdout from malloc before the group
|
|
* data has been allocated. The other other is (2) if this is a kernel
|
|
* thread. Kernel threads have no allocated file descriptors.
|
|
*/
|
|
|
|
if (list == NULL)
|
|
{
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (fd < 0 || fd >= list->fl_rows * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
/* The descriptor is in a valid range to file descriptor... Get the
|
|
* thread-specific file list.
|
|
*/
|
|
|
|
/* And return the file pointer from the list */
|
|
|
|
ret = _files_semtake(list);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
*filep = &list->fl_files[fd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK]
|
|
[fd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK];
|
|
|
|
/* if f_inode is NULL, fd was closed */
|
|
|
|
if (!(*filep)->f_inode)
|
|
{
|
|
*filep = (FAR struct file *)NULL;
|
|
ret = -EBADF;
|
|
}
|
|
|
|
_files_semgive(list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nx_dup2
|
|
*
|
|
* Description:
|
|
* nx_dup2() is similar to the standard 'dup2' interface except that is
|
|
* not a cancellation point and it does not modify the errno variable.
|
|
*
|
|
* nx_dup2() is an internal NuttX interface and should not be called from
|
|
* applications.
|
|
*
|
|
* Clone a file descriptor to a specific descriptor number.
|
|
*
|
|
* Returned Value:
|
|
* fd2 is returned on success; a negated errno value is return on
|
|
* any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nx_dup2(int fd1, int fd2)
|
|
{
|
|
FAR struct filelist *list;
|
|
FAR struct file *filep;
|
|
FAR struct file file;
|
|
int ret;
|
|
|
|
if (fd1 == fd2)
|
|
{
|
|
return fd1;
|
|
}
|
|
|
|
/* Get the file descriptor list. It should not be NULL in this context. */
|
|
|
|
list = nxsched_get_files();
|
|
DEBUGASSERT(list != NULL);
|
|
|
|
if (fd1 < 0 || fd1 >= CONFIG_NFILE_DESCRIPTORS_PER_BLOCK * list->fl_rows ||
|
|
fd2 < 0)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
ret = _files_semtake(list);
|
|
if (ret < 0)
|
|
{
|
|
/* Probably canceled */
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (fd2 >= CONFIG_NFILE_DESCRIPTORS_PER_BLOCK * list->fl_rows)
|
|
{
|
|
ret = files_extend(list, fd2 / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + 1);
|
|
if (ret < 0)
|
|
{
|
|
_files_semgive(list);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
filep = &list->fl_files[fd2 / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK]
|
|
[fd2 % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK];
|
|
memcpy(&file, filep, sizeof(struct file));
|
|
memset(filep, 0, sizeof(struct file));
|
|
|
|
/* Perform the dup2 operation */
|
|
|
|
ret = file_dup2(&list->fl_files[fd1 / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK]
|
|
[fd1 % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK],
|
|
filep);
|
|
_files_semgive(list);
|
|
|
|
file_close(&file);
|
|
|
|
return ret < 0 ? ret : fd2;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dup2
|
|
*
|
|
* Description:
|
|
* Clone a file descriptor or socket descriptor to a specific descriptor
|
|
* number
|
|
*
|
|
****************************************************************************/
|
|
|
|
int dup2(int fd1, int fd2)
|
|
{
|
|
int ret;
|
|
|
|
ret = nx_dup2(fd1, fd2);
|
|
if (ret < 0)
|
|
{
|
|
set_errno(-ret);
|
|
ret = ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nx_close
|
|
*
|
|
* Description:
|
|
* nx_close() is similar to the standard 'close' interface except that is
|
|
* not a cancellation point and it does not modify the errno variable.
|
|
*
|
|
* nx_close() is an internal NuttX interface and should not be called from
|
|
* applications.
|
|
*
|
|
* Close an inode (if open)
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) is returned on success; A negated errno value is returned on
|
|
* on any failure.
|
|
*
|
|
* Assumptions:
|
|
* Caller holds the list semaphore because the file descriptor will be
|
|
* freed.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nx_close(int fd)
|
|
{
|
|
FAR struct filelist *list;
|
|
FAR struct file *filep;
|
|
FAR struct file file;
|
|
int ret;
|
|
|
|
/* Get the thread-specific file list. It should never be NULL in this
|
|
* context.
|
|
*/
|
|
|
|
list = nxsched_get_files();
|
|
DEBUGASSERT(list != NULL);
|
|
|
|
/* Perform the protected close operation */
|
|
|
|
ret = _files_semtake(list);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* If the file was properly opened, there should be an inode assigned */
|
|
|
|
if (fd < 0 || fd >= list->fl_rows * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK ||
|
|
!list->fl_files[fd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK]
|
|
[fd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK].f_inode)
|
|
{
|
|
_files_semgive(list);
|
|
return -EBADF;
|
|
}
|
|
|
|
filep = &list->fl_files[fd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK]
|
|
[fd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK];
|
|
memcpy(&file, filep, sizeof(struct file));
|
|
memset(filep, 0, sizeof(struct file));
|
|
|
|
_files_semgive(list);
|
|
|
|
return file_close(&file);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: close
|
|
*
|
|
* Description:
|
|
* close() closes a file descriptor, so that it no longer refers to any
|
|
* file and may be reused. Any record locks (see fcntl(2)) held on the file
|
|
* it was associated with, and owned by the process, are removed
|
|
* (regardless of the file descriptor that was used to obtain the lock).
|
|
*
|
|
* If fd is the last copy of a particular file descriptor the resources
|
|
* associated with it are freed; if the descriptor was the last reference
|
|
* to a file which has been removed using unlink(2) the file is deleted.
|
|
*
|
|
* Input Parameters:
|
|
* fd file descriptor to close
|
|
*
|
|
* Returned Value:
|
|
* 0 on success; -1 on error with errno set appropriately.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
int close(int fd)
|
|
{
|
|
int ret;
|
|
|
|
/* close() is a cancellation point */
|
|
|
|
enter_cancellation_point();
|
|
|
|
ret = nx_close(fd);
|
|
if (ret < 0)
|
|
{
|
|
set_errno(-ret);
|
|
ret = ERROR;
|
|
}
|
|
|
|
leave_cancellation_point();
|
|
return ret;
|
|
}
|