/* Copyright (C) 2015 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include #include #include #include #include #include #include #include #include extern void *__fd_properties_cleanup_hook; static void open_fd(fd_properties *fd, int open_flags); static void close_fd(fd_properties *fd); static fd_properties * find_eq_filename(const fd_properties *fd); static fd_properties * alloc_fd_properties(void); static void free_fd_properties(fd_properties *); static void insert_list(fd_properties **, fd_properties *); static void remove_list(fd_properties **, fd_properties *); static void exit_cleanup(void); /* Array of pointers to fd_properties objects associated with a file descriptor. */ fd_properties **__fd_properties = NULL; /* List of fd_properties objects associated with at least one file descriptor. */ static fd_properties *active_fds; /* List of fd_properties objects not currently in use. */ static fd_properties *cached_fds; static int old_bss_count; /* Perform file descriptor specific initialization. */ static inline void open_fd(fd_properties *cur_fd, int open_flags) { cur_fd->flags = 0; if (cur_fd->filename && (open_flags & O_TEMPORARY)) cur_fd->flags |= FILE_DESC_TEMPORARY; if (open_flags & O_APPEND) cur_fd->flags |= FILE_DESC_APPEND; } /* Perform file descriptor specific finalization. */ static inline void close_fd(fd_properties *cur_fd) { /* Delete the file if no other descriptors use the file. Otherwise, mark the other descriptor with the temporary flag. */ if (cur_fd->flags & FILE_DESC_TEMPORARY) { fd_properties *ptr = find_eq_filename(cur_fd); if (ptr) ptr->flags |= FILE_DESC_TEMPORARY; else remove(cur_fd->filename); } } /* Set properties associated with a file descriptor. */ int __set_fd_properties(int fd, const char *file, int open_flags) { char *result; if (old_bss_count != __bss_count) { size_t size = 255 * sizeof(fd_properties *); old_bss_count = __bss_count; __fd_properties = (fd_properties**)malloc(size); active_fds = NULL; cached_fds = NULL; if (__fd_properties == NULL) return -1; memset(__fd_properties, 0, size); __fd_properties_cleanup_hook = (void *)exit_cleanup; } /* This function may be called twice for the same fd, so allocate and initialize when not already done.*/ if (__fd_properties[fd] == NULL) { __fd_properties[fd] = alloc_fd_properties(); if (__fd_properties[fd] == NULL) return -1; /* Initialize the object and insert it into list. */ __fd_properties[fd]->ref_count = 1; result = _truename(file, NULL); __fd_properties[fd]->filename = result; insert_list(&active_fds, __fd_properties[fd]); } open_fd(__fd_properties[fd], open_flags); return 0; } /* Set properties of a file descriptor returned by dup or dup2. */ void __dup_fd_properties(int from, int to) { if (__fd_properties[from]) { __fd_properties[to] = __fd_properties[from]; ++(__fd_properties[to]->ref_count); } } /* Clear properties associated with a file descriptor. */ int __clear_fd_properties(int fd) { /* If there are no properties with this descriptor then punt. */ if (__fd_properties[fd] == NULL) return -1; if (--(__fd_properties[fd]->ref_count) == 0) { /* The last file descriptor using this object has closed. Perform any final actions before the object is put into the cache. */ close_fd(__fd_properties[fd]); free(__fd_properties[fd]->filename); __fd_properties[fd]->filename = NULL; free_fd_properties(__fd_properties[fd]); } __fd_properties[fd] = NULL; return 0; } /* Find another properties object using the same filename. */ static fd_properties * find_eq_filename(const fd_properties *fd) { fd_properties *ptr = active_fds; while (ptr) { if ((ptr != fd) && (stricmp(fd->filename, ptr->filename) == 0)) return ptr; ptr = ptr->next; } return NULL; } /* Return a properties object for use with one or more file descriptors. */ static fd_properties * alloc_fd_properties(void) { fd_properties *ptr; if (cached_fds == NULL) { ptr = (fd_properties*)malloc(sizeof(fd_properties)); if (ptr == NULL) return ptr; } else { ptr = cached_fds; remove_list(&cached_fds, cached_fds); } ptr->prev = NULL; ptr->next = NULL; ptr->ref_count = 1; return ptr; } /* Remove the object from the active list, and insert it into the cache. */ static void free_fd_properties(fd_properties *fd) { remove_list(&active_fds, fd); insert_list(&cached_fds, fd); } /* Insert a properties object into a list. */ static void insert_list(fd_properties **head_ptr, fd_properties *item) { fd_properties *head = *head_ptr; item->next = head; item->prev = NULL; if (head) head->prev = item; *head_ptr = item; } /* Remove a properties object from a list. */ static void remove_list(fd_properties **head_ptr, fd_properties *item) { fd_properties *head = *head_ptr; if (item->prev) { (item->prev)->next = item->next; item->prev = NULL; } if (item->next) { (item->next)->prev = item->prev; item->next = NULL; } if (head == item) head = head->next; *head_ptr = head; } /* Close all known file descriptors to ensure all files marked as temporary are deleted at exit. */ static void exit_cleanup(void) { int fd; if (__fd_properties == NULL || active_fds == NULL) return; fd = 0; while (fd < 255) { if (__fd_properties[fd]) _close(fd); ++fd; } }