| /**************************************************************************** |
| * 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 <execinfo.h> |
| #include <sched.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <debug.h> |
| #include <stdio.h> |
| |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/cancelpt.h> |
| #include <nuttx/fs/ioctl.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/sched.h> |
| #include <nuttx/spawn.h> |
| #include <nuttx/spinlock.h> |
| |
| #ifdef CONFIG_FDSAN |
| # include <android/fdsan.h> |
| #endif |
| |
| #ifdef CONFIG_FDCHECK |
| # include <nuttx/fdcheck.h> |
| #endif |
| |
| #include "inode/inode.h" |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: files_fget_by_index |
| ****************************************************************************/ |
| |
| static FAR struct file *files_fget_by_index(FAR struct filelist *list, |
| int l1, int l2) |
| { |
| FAR struct file *filep; |
| irqstate_t flags; |
| |
| flags = spin_lock_irqsave(NULL); |
| |
| filep = &list->fl_files[l1][l2]; |
| |
| spin_unlock_irqrestore(NULL, flags); |
| |
| return filep; |
| } |
| |
| /**************************************************************************** |
| * Name: files_extend |
| ****************************************************************************/ |
| |
| static int files_extend(FAR struct filelist *list, size_t row) |
| { |
| FAR struct file **files; |
| uint8_t orig_rows; |
| FAR void *tmp; |
| int flags; |
| int i; |
| int j; |
| |
| if (row <= list->fl_rows) |
| { |
| return 0; |
| } |
| |
| if (files_countlist(list) > OPEN_MAX) |
| { |
| return -EMFILE; |
| } |
| |
| orig_rows = list->fl_rows; |
| |
| files = kmm_malloc(sizeof(FAR struct file *) * row); |
| DEBUGASSERT(files); |
| if (files == NULL) |
| { |
| return -ENFILE; |
| } |
| |
| i = list->fl_rows; |
| do |
| { |
| files[i] = kmm_zalloc(sizeof(struct file) * |
| CONFIG_NFILE_DESCRIPTORS_PER_BLOCK); |
| if (files[i] == NULL) |
| { |
| while (--i >= list->fl_rows) |
| { |
| kmm_free(files[i]); |
| } |
| |
| kmm_free(files); |
| return -ENFILE; |
| } |
| } |
| while (++i < row); |
| |
| flags = spin_lock_irqsave(NULL); |
| |
| /* To avoid race condition, if the file list is updated by other threads |
| * and list rows is greater or equal than temp list, |
| * release the obsolete buffers |
| */ |
| |
| if (orig_rows != list->fl_rows && list->fl_rows >= row) |
| { |
| spin_unlock_irqrestore(NULL, flags); |
| |
| for (j = orig_rows; j < i; j++) |
| { |
| kmm_free(files[i]); |
| } |
| |
| kmm_free(files); |
| |
| return OK; |
| } |
| |
| if (list->fl_files != NULL) |
| { |
| memcpy(files, list->fl_files, |
| list->fl_rows * sizeof(FAR struct file *)); |
| } |
| |
| tmp = list->fl_files; |
| list->fl_files = files; |
| list->fl_rows = row; |
| |
| spin_unlock_irqrestore(NULL, flags); |
| |
| if (tmp != NULL && tmp != &list->fl_prefile) |
| { |
| kmm_free(tmp); |
| } |
| |
| return OK; |
| } |
| |
| static void task_fssync(FAR struct tcb_s *tcb, FAR void *arg) |
| { |
| FAR struct filelist *list; |
| int i; |
| int j; |
| |
| list = &tcb->group->tg_filelist; |
| |
| for (i = 0; i < list->fl_rows; i++) |
| { |
| for (j = 0; j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; j++) |
| { |
| FAR struct file *filep; |
| |
| filep = files_fget_by_index(list, i, j); |
| if (filep->f_inode != NULL) |
| { |
| file_fsync(filep); |
| } |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nx_dup3_from_tcb |
| * |
| * Description: |
| * nx_dup3_from_tcb() is similar to the standard 'dup3' interface |
| * except that is not a cancellation point and it does not modify the |
| * errno variable. |
| * |
| * nx_dup3_from_tcb() is an internal NuttX interface and should not be |
| * called from applications. |
| * |
| * Clone a file descriptor to a specific descriptor number and |
| * specific flags. |
| * |
| * Returned Value: |
| * fd2 is returned on success; a negated errno value is return on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int nx_dup3_from_tcb(FAR struct tcb_s *tcb, int fd1, int fd2, |
| int flags) |
| { |
| FAR struct filelist *list; |
| FAR struct file *filep; |
| #ifdef CONFIG_FDCHECK |
| uint8_t f_tag_fdcheck; |
| #endif |
| #ifdef CONFIG_FDSAN |
| uint64_t f_tag_fdsan; |
| #endif |
| int count; |
| int ret; |
| |
| if (fd1 == fd2) |
| { |
| return fd1; |
| } |
| |
| #ifdef CONFIG_FDCHECK |
| fd1 = fdcheck_restore(fd1); |
| fd2 = fdcheck_restore(fd2); |
| #endif |
| |
| /* Get the file descriptor list. It should not be NULL in this context. */ |
| |
| list = nxsched_get_files_from_tcb(tcb); |
| count = files_countlist(list); |
| |
| if (fd1 < 0 || fd1 >= count || fd2 < 0) |
| { |
| return -EBADF; |
| } |
| |
| if (fd2 >= count) |
| { |
| ret = files_extend(list, fd2 / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + 1); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| } |
| |
| filep = files_fget(list, fd2); |
| if (filep == NULL) |
| { |
| return -EBADF; |
| } |
| |
| #ifdef CONFIG_FDSAN |
| f_tag_fdsan = filep->f_tag_fdsan; |
| #endif |
| |
| #ifdef CONFIG_FDCHECK |
| f_tag_fdcheck = filep->f_tag_fdcheck; |
| #endif |
| |
| /* Perform the dup3 operation */ |
| |
| ret = file_dup3(files_fget(list, fd1), filep, flags); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| #ifdef CONFIG_FDSAN |
| filep->f_tag_fdsan = f_tag_fdsan; |
| #endif |
| |
| #ifdef CONFIG_FDCHECK |
| filep->f_tag_fdcheck = f_tag_fdcheck; |
| #endif |
| |
| #ifdef CONFIG_FDCHECK |
| return fdcheck_protect(fd2); |
| #else |
| return fd2; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: files_initlist |
| * |
| * Description: Initializes the list of files for a new task |
| * |
| ****************************************************************************/ |
| |
| void files_initlist(FAR struct filelist *list) |
| { |
| /* The first row will reuse pre-allocated files, which will avoid |
| * unnecessary allocator accesses during file initialization. |
| */ |
| |
| list->fl_rows = 1; |
| list->fl_files = &list->fl_prefile; |
| list->fl_prefile = list->fl_prefiles; |
| } |
| |
| /**************************************************************************** |
| * Name: files_dumplist |
| * |
| * Description: |
| * Dump the list of files. |
| * |
| ****************************************************************************/ |
| |
| void files_dumplist(FAR struct filelist *list) |
| { |
| int count = files_countlist(list); |
| int i; |
| |
| syslog(LOG_INFO, "%-4s%-4s%-8s%-5s%-10s%-14s" |
| #if CONFIG_FS_BACKTRACE > 0 |
| " BACKTRACE" |
| #endif |
| "\n", |
| "PID", "FD", "FLAGS", "TYPE", "POS", "PATH" |
| ); |
| |
| for (i = 0; i < count; i++) |
| { |
| FAR struct file *filep = files_fget(list, i); |
| char path[PATH_MAX]; |
| |
| #if CONFIG_FS_BACKTRACE > 0 |
| char buf[BACKTRACE_BUFFER_SIZE(CONFIG_FS_BACKTRACE)]; |
| #endif |
| |
| /* Is there an inode associated with the file descriptor? */ |
| |
| if (filep == NULL || filep->f_inode == NULL) |
| { |
| continue; |
| } |
| |
| if (file_ioctl(filep, FIOC_FILEPATH, path) < 0) |
| { |
| path[0] = '\0'; |
| } |
| |
| #if CONFIG_FS_BACKTRACE > 0 |
| backtrace_format(buf, sizeof(buf), filep->f_backtrace, |
| CONFIG_FS_BACKTRACE); |
| #endif |
| |
| syslog(LOG_INFO, "%-4d%-4d%-8d%-5x%-10ld%-14s" |
| #if CONFIG_FS_BACKTRACE > 0 |
| " %s" |
| #endif |
| "\n", getpid(), i, filep->f_oflags, |
| INODE_GET_TYPE(filep->f_inode), |
| (long)filep->f_pos, path |
| #if CONFIG_FS_BACKTRACE > 0 |
| , buf |
| #endif |
| ); |
| } |
| } |
| |
| /**************************************************************************** |
| * 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 |
| * mutex, but it is safe to ignore the mutex 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]); |
| } |
| |
| if (i != 0) |
| { |
| kmm_free(list->fl_files[i]); |
| } |
| } |
| |
| if (list->fl_files != &list->fl_prefile) |
| { |
| kmm_free(list->fl_files); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: files_countlist |
| * |
| * 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 files_countlist(FAR struct filelist *list) |
| { |
| return list->fl_rows * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; |
| } |
| |
| /**************************************************************************** |
| * Name: files_fget |
| * |
| * Description: |
| * Get the instance of struct file from file list by file descriptor. |
| * |
| * Input Parameters: |
| * list - The list of files for a task. |
| * fd - A valid descriptor between 0 and files_countlist(list). |
| * |
| * Returned Value: |
| * Pointer to file structure of list[fd]. |
| * |
| ****************************************************************************/ |
| |
| FAR struct file *files_fget(FAR struct filelist *list, int fd) |
| { |
| return files_fget_by_index(list, fd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK, |
| fd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK); |
| } |
| |
| /**************************************************************************** |
| * Name: file_allocate_from_tcb |
| * |
| * Description: |
| * Allocate a struct files instance and associate it with an inode |
| * instance. |
| * |
| * Returned Value: |
| * Returns the file descriptor == index into the files array on success; |
| * a negated errno value is returned on any failure. |
| * |
| ****************************************************************************/ |
| |
| int file_allocate_from_tcb(FAR struct tcb_s *tcb, FAR struct inode *inode, |
| int oflags, off_t pos, FAR void *priv, int minfd, |
| bool addref) |
| { |
| int i = minfd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; |
| int j = minfd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; |
| FAR struct filelist *list; |
| FAR struct file *filep; |
| irqstate_t flags; |
| int ret; |
| |
| /* Get the file descriptor list. It should not be NULL in this context. */ |
| |
| list = nxsched_get_files_from_tcb(tcb); |
| |
| /* Find free file */ |
| |
| flags = spin_lock_irqsave(NULL); |
| |
| for (; ; i++, j = 0) |
| { |
| if (i >= list->fl_rows) |
| { |
| spin_unlock_irqrestore(NULL, flags); |
| |
| ret = files_extend(list, i + 1); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| flags = spin_lock_irqsave(NULL); |
| } |
| |
| do |
| { |
| filep = &list->fl_files[i][j]; |
| if (filep->f_inode == NULL) |
| { |
| filep->f_oflags = oflags; |
| filep->f_pos = pos; |
| filep->f_inode = inode; |
| filep->f_priv = priv; |
| |
| goto found; |
| } |
| } |
| while (++j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK); |
| } |
| |
| found: |
| spin_unlock_irqrestore(NULL, flags); |
| |
| if (addref) |
| { |
| inode_addref(inode); |
| } |
| |
| FS_ADD_BACKTRACE(filep); |
| |
| #ifdef CONFIG_FDCHECK |
| return fdcheck_protect(i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j); |
| #else |
| return i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: file_allocate |
| * |
| * Description: |
| * Allocate a struct files instance and associate it with an inode |
| * instance. |
| * |
| * Returned Value: |
| * Returns the file descriptor == index into the files array on success; |
| * a negated errno value is returned on any failure. |
| * |
| ****************************************************************************/ |
| |
| int file_allocate(FAR struct inode *inode, int oflags, off_t pos, |
| FAR void *priv, int minfd, bool addref) |
| { |
| return file_allocate_from_tcb(nxsched_self(), inode, oflags, |
| pos, priv, minfd, addref); |
| } |
| |
| /**************************************************************************** |
| * Name: files_duplist |
| * |
| * Description: |
| * Duplicate parent task's file descriptors. |
| * |
| ****************************************************************************/ |
| |
| int files_duplist(FAR struct filelist *plist, FAR struct filelist *clist, |
| FAR const posix_spawn_file_actions_t *actions, |
| bool cloexec) |
| { |
| bool fcloexec; |
| int ret; |
| int fd; |
| int i; |
| int j; |
| |
| for (i = 0; i < plist->fl_rows; i++) |
| { |
| for (j = 0; j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; j++) |
| { |
| FAR struct file *filep; |
| |
| fd = i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j; |
| #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 (fd >= 3) |
| { |
| return OK; |
| } |
| #endif |
| |
| filep = files_fget_by_index(plist, i, j); |
| if (filep->f_inode == NULL) |
| { |
| continue; |
| } |
| |
| fcloexec = (cloexec && (filep->f_oflags & O_CLOEXEC)); |
| |
| /* Skip file dup if file action is unnecessary to duplicate */ |
| |
| if (actions != NULL) |
| { |
| #ifdef CONFIG_FDCHECK |
| fd = fdcheck_protect(fd); |
| #endif |
| if (!spawn_file_is_duplicateable(actions, fd, fcloexec)) |
| { |
| continue; |
| } |
| } |
| else if (fcloexec) |
| { |
| continue; |
| } |
| |
| ret = files_extend(clist, i + 1); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Yes... duplicate it for the child, include O_CLOEXEC flag. */ |
| |
| ret = file_dup2(filep, files_fget_by_index(clist, i, j)); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| } |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * 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; |
| |
| #ifdef CONFIG_FDCHECK |
| fd = fdcheck_restore(fd); |
| #endif |
| |
| *filep = 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 >= files_countlist(list)) |
| { |
| return -EBADF; |
| } |
| |
| /* The descriptor is in a valid range to file descriptor... Get the |
| * thread-specific file list. |
| */ |
| |
| *filep = files_fget(list, fd); |
| |
| /* if f_inode is NULL, fd was closed */ |
| |
| if ((*filep)->f_inode == NULL) |
| { |
| *filep = NULL; |
| return -EBADF; |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nx_dup2_from_tcb |
| * |
| * Description: |
| * nx_dup2_from_tcb() is similar to the standard 'dup2' interface |
| * except that is not a cancellation point and it does not modify the |
| * errno variable. |
| * |
| * nx_dup2_from_tcb() 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_from_tcb(FAR struct tcb_s *tcb, int fd1, int fd2) |
| { |
| return nx_dup3_from_tcb(tcb, fd1, fd2, 0); |
| } |
| |
| /**************************************************************************** |
| * 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) |
| { |
| return nx_dup2_from_tcb(nxsched_self(), fd1, 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: dup3 |
| * |
| * Description: |
| * Clone a file descriptor or socket descriptor to a specific descriptor |
| * number and specific flags. |
| * |
| ****************************************************************************/ |
| |
| int dup3(int fd1, int fd2, int flags) |
| { |
| int ret; |
| |
| ret = nx_dup3_from_tcb(nxsched_self(), fd1, fd2, flags); |
| if (ret < 0) |
| { |
| set_errno(-ret); |
| ret = ERROR; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nx_close_from_tcb |
| * |
| * Description: |
| * nx_close_from_tcb() is similar to the standard 'close' interface |
| * except that is not a cancellation point and it does not modify the |
| * errno variable. |
| * |
| * nx_close_from_tcb() 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 mutex because the file descriptor will be |
| * freed. |
| * |
| ****************************************************************************/ |
| |
| int nx_close_from_tcb(FAR struct tcb_s *tcb, int fd) |
| { |
| FAR struct file *filep; |
| FAR struct filelist *list; |
| |
| #ifdef CONFIG_FDCHECK |
| fd = fdcheck_restore(fd); |
| #endif |
| |
| list = nxsched_get_files_from_tcb(tcb); |
| |
| /* Perform the protected close operation */ |
| |
| if (fd < 0 || fd >= files_countlist(list)) |
| { |
| return -EBADF; |
| } |
| |
| filep = files_fget(list, fd); |
| |
| /* If the file was properly opened, there should be an inode assigned */ |
| |
| if (filep->f_inode == NULL) |
| { |
| return -EBADF; |
| } |
| |
| return file_close(filep); |
| } |
| |
| /**************************************************************************** |
| * 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 mutex because the file descriptor will be |
| * freed. |
| * |
| ****************************************************************************/ |
| |
| int nx_close(int fd) |
| { |
| return nx_close_from_tcb(nxsched_self(), fd); |
| } |
| |
| /**************************************************************************** |
| * 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; |
| |
| #ifdef CONFIG_FDSAN |
| android_fdsan_exchange_owner_tag(fd, 0, 0); |
| #endif |
| |
| /* 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; |
| } |
| |
| /**************************************************************************** |
| * Name: sync |
| * |
| * Description: |
| * sync() causes all pending modifications to filesystem metadata and |
| * cached file data to be written to the underlying filesystems. |
| * |
| ****************************************************************************/ |
| |
| void sync(void) |
| { |
| nxsched_foreach(task_fssync, NULL); |
| } |