]> Kevux Git Server - fll/commitdiff
Bugfix: Fixes for f_file exposed by unit tests.
authorKevin Day <thekevinday@gmail.com>
Thu, 21 Apr 2022 03:54:39 +0000 (22:54 -0500)
committerKevin Day <thekevinday@gmail.com>
Thu, 21 Apr 2022 03:54:56 +0000 (22:54 -0500)
Fix case where private_f_file_stat_at() is being passed F_true rather than the flag.

Add missing path.used checks.

When total is 0 in f_file_stream_read_until(), then immediately return as F_none_stop.

Handle case where freopen() actually allows for the path to be NULL in which case the mode string is applied.
In this case, return F_data_not only if both path and mode are not used.
Always re-assign the file stream after calling freopen().

The return status' from private_f_file_stream_write_until() calls are not being processed.
The code is checking the values but the value is never assigned.
Add the missing return value assignment.

Move file stream locking into private_f_file_stream_write_until() and make sure only unlocked functions are used.

The f_file_type() and f_file_type_at() functions need to accept a dereference boolean for consistency with the rest of the project.
Have these two functions call private_f_file_stat() and private_f_file_stat_at() respectively.

When fwrite_unlocked() is called, be sure to set check if ferror_unlocked() returns an error rather than checking size_write.
The previous behavior is incorrect because it is checking if size is less than 0 and the man pages claim that fwrite()/fwrite_unlocked() returns nothing smaller than 0 on failure.

Have the fwrite_unlocked() unknown errnor codes return F_file_write rather than F_failure.

Clean up function ordering.

level_0/f_file/c/file.c
level_0/f_file/c/file.h
level_0/f_file/c/private-file.c

index 3c0818634da716ff54a2f1903d03cd87ac6d42a3..d6bb58327b6ecde55d1f504ccfb97b3c31b39ea8 100644 (file)
@@ -1373,7 +1373,7 @@ extern "C" {
     memset(&stat_file, 0, sizeof(struct stat));
 
     {
-      const f_status_t status = private_f_file_stat_at(at_id, path, F_true, &stat_file);
+      const f_status_t status = private_f_file_stat_at(at_id, path, flag, &stat_file);
       if (F_status_is_error(status)) return status;
     }
 
@@ -2077,97 +2077,101 @@ extern "C" {
   }
 #endif // _di_f_file_stream_close_
 
-#ifndef _di_f_file_stream_open_descriptor_
-  f_status_t f_file_stream_open_descriptor(const f_string_static_t mode, f_file_t * const file) {
+#ifndef _di_f_file_stream_open_
+  f_status_t f_file_stream_open(const f_string_static_t path, const f_string_static_t mode, f_file_t * const file) {
     #ifndef _di_level_0_parameter_checking_
       if (!file) return F_status_set_error(F_parameter);
     #endif // _di_level_0_parameter_checking_
 
-    if (file->id == -1) {
-      return F_status_set_error(F_file_descriptor);
+    if (!path.used) {
+      return F_data_not;
     }
 
     if (mode.used) {
-      file->stream = fdopen(file->id, private_f_file_stream_open_mode_determine(file->flag));
+      file->stream = fopen(path.string, mode.string);
     }
     else {
-      file->stream = fdopen(file->id, mode.string);
+      file->stream = fopen(path.string, private_f_file_stream_open_mode_determine(file->flag));
     }
 
     if (!file->stream) {
       if (errno == EACCES) return F_status_set_error(F_access_denied);
-      if (errno == EAGAIN || errno == EWOULDBLOCK) return F_status_set_error(F_block);
-      if (errno == EBADF) return F_status_set_error(F_file_descriptor);
-      if (errno == EFBIG) return F_status_set_error(F_file_overflow);
-      if (errno == EDEADLK) return F_status_set_error(F_deadlock);
-      if (errno == EDESTADDRREQ) return F_status_set_error(F_socket_not);
-      if (errno == EDQUOT) return F_status_set_error(F_space_not);
+      if (errno == EDQUOT) return F_status_set_error(F_filesystem_quota_block);
+      if (errno == EEXIST) return F_status_set_error(F_file_found);
+      if (errno == ENAMETOOLONG) return F_status_set_error(F_name);
       if (errno == EFAULT) return F_status_set_error(F_buffer);
+      if (errno == EFBIG || errno == EOVERFLOW) return F_status_set_error(F_number_overflow);
       if (errno == EINTR) return F_status_set_error(F_interrupt);
       if (errno == EINVAL) return F_status_set_error(F_parameter);
-      if (errno == EIO) return F_status_set_error(F_input_output);
-      if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max);
-      if (errno == ENODEV) return F_status_set_error(F_device_not);
-      if (errno == ENOLCK) return F_status_set_error(F_lock);
+      if (errno == ELOOP) return F_status_set_error(F_loop);
+      if (errno == ENFILE) return F_status_set_error(F_file_open_max);
+      if (errno == ENOENT) return F_status_set_error(F_file_found_not);
+      if (errno == ENOTDIR) return F_status_set_error(F_file_type_not_directory);
       if (errno == ENOMEM) return F_status_set_error(F_memory_not);
       if (errno == ENOSPC) return F_status_set_error(F_space_not);
-      if (errno == ENOTDIR) return F_status_set_error(F_file_type_not_directory);
       if (errno == EPERM) return F_status_set_error(F_prohibited);
-      if (errno == EPIPE) return F_status_set_error(F_pipe_not);
+      if (errno == EROFS) return F_status_set_error(F_read_only);
+      if (errno == ETXTBSY) return F_status_set_error(F_busy);
+      if (errno == EISDIR) return F_status_set_error(F_directory);
+      if (errno == EOPNOTSUPP) return F_status_set_error(F_supported_not);
 
       return F_status_set_error(F_failure);
     }
 
+    file->id = fileno(file->stream);
+
+    if (file->id == -1) {
+      return F_status_set_error(F_file_descriptor);
+    }
+
     return F_none;
   }
-#endif // _di_f_file_stream_open_descriptor_
+#endif // _di_f_file_stream_open_
 
-#ifndef _di_f_file_stream_open_
-  f_status_t f_file_stream_open(const f_string_static_t path, const f_string_static_t mode, f_file_t * const file) {
+#ifndef _di_f_file_stream_open_descriptor_
+  f_status_t f_file_stream_open_descriptor(const f_string_static_t mode, f_file_t * const file) {
     #ifndef _di_level_0_parameter_checking_
       if (!file) return F_status_set_error(F_parameter);
     #endif // _di_level_0_parameter_checking_
 
+    if (file->id == -1) {
+      return F_status_set_error(F_file_descriptor);
+    }
+
     if (mode.used) {
-      file->stream = fopen(path.string, mode.string);
+      file->stream = fdopen(file->id, private_f_file_stream_open_mode_determine(file->flag));
     }
     else {
-      file->stream = fopen(path.string, private_f_file_stream_open_mode_determine(file->flag));
+      file->stream = fdopen(file->id, mode.string);
     }
 
     if (!file->stream) {
       if (errno == EACCES) return F_status_set_error(F_access_denied);
-      if (errno == EDQUOT) return F_status_set_error(F_filesystem_quota_block);
-      if (errno == EEXIST) return F_status_set_error(F_file_found);
-      if (errno == ENAMETOOLONG) return F_status_set_error(F_name);
+      if (errno == EAGAIN || errno == EWOULDBLOCK) return F_status_set_error(F_block);
+      if (errno == EBADF) return F_status_set_error(F_file_descriptor);
+      if (errno == EFBIG) return F_status_set_error(F_file_overflow);
+      if (errno == EDEADLK) return F_status_set_error(F_deadlock);
+      if (errno == EDESTADDRREQ) return F_status_set_error(F_socket_not);
+      if (errno == EDQUOT) return F_status_set_error(F_space_not);
       if (errno == EFAULT) return F_status_set_error(F_buffer);
-      if (errno == EFBIG || errno == EOVERFLOW) return F_status_set_error(F_number_overflow);
       if (errno == EINTR) return F_status_set_error(F_interrupt);
       if (errno == EINVAL) return F_status_set_error(F_parameter);
-      if (errno == ELOOP) return F_status_set_error(F_loop);
-      if (errno == ENFILE) return F_status_set_error(F_file_open_max);
-      if (errno == ENOENT) return F_status_set_error(F_file_found_not);
-      if (errno == ENOTDIR) return F_status_set_error(F_file_type_not_directory);
+      if (errno == EIO) return F_status_set_error(F_input_output);
+      if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max);
+      if (errno == ENODEV) return F_status_set_error(F_device_not);
+      if (errno == ENOLCK) return F_status_set_error(F_lock);
       if (errno == ENOMEM) return F_status_set_error(F_memory_not);
       if (errno == ENOSPC) return F_status_set_error(F_space_not);
+      if (errno == ENOTDIR) return F_status_set_error(F_file_type_not_directory);
       if (errno == EPERM) return F_status_set_error(F_prohibited);
-      if (errno == EROFS) return F_status_set_error(F_read_only);
-      if (errno == ETXTBSY) return F_status_set_error(F_busy);
-      if (errno == EISDIR) return F_status_set_error(F_directory);
-      if (errno == EOPNOTSUPP) return F_status_set_error(F_supported_not);
+      if (errno == EPIPE) return F_status_set_error(F_pipe_not);
 
       return F_status_set_error(F_failure);
     }
 
-    file->id = fileno(file->stream);
-
-    if (file->id == -1) {
-      return F_status_set_error(F_file_descriptor);
-    }
-
     return F_none;
   }
-#endif // _di_f_file_stream_open_
+#endif // _di_f_file_stream_open_descriptor_
 
 #ifndef _di_f_file_stream_read_
   f_status_t f_file_stream_read(const f_file_t file, f_string_dynamic_t * const buffer) {
@@ -2310,6 +2314,10 @@ extern "C" {
       return F_status_set_error(F_file_closed);
     }
 
+    if (!total) {
+      return F_none_stop;
+    }
+
     flockfile(file.stream);
 
     if (feof_unlocked(file.stream)) {
@@ -2386,17 +2394,17 @@ extern "C" {
       if (!file) return F_status_set_error(F_parameter);
     #endif // _di_level_0_parameter_checking_
 
-    if (!path.used) {
+    if (!path.used && !mode.used) {
       return F_data_not;
     }
 
     FILE *result = 0;
 
     if (mode.used) {
-      result = freopen(path.string, mode.string, file->stream);
+      result = freopen(path.used ? path.string : 0, mode.string, file->stream);
     }
     else {
-      result = freopen(path.string, private_f_file_stream_open_mode_determine(file->flag), file->stream);
+      result = freopen(path.used ? path.string : 0, private_f_file_stream_open_mode_determine(file->flag), file->stream);
     }
 
     if (!result) {
@@ -2421,6 +2429,7 @@ extern "C" {
       return F_status_set_error(F_failure);
     }
 
+    file->stream = result;
     file->id = fileno(file->stream);
 
     if (file->id == -1) {
@@ -2452,7 +2461,7 @@ extern "C" {
     f_status_t status = F_none;
 
     if (written) {
-      private_f_file_stream_write_until(file, buffer, buffer.used, written);
+      status = private_f_file_stream_write_until(file, buffer, buffer.used, written);
 
       if (status == F_none && *written == buffer.used) {
         return F_none_eos;
@@ -2461,17 +2470,13 @@ extern "C" {
     else {
       f_array_length_t written_local = 0;
 
-      private_f_file_stream_write_until(file, buffer, buffer.used, &written_local);
+      status = private_f_file_stream_write_until(file, buffer, buffer.used, &written_local);
 
       if (status == F_none && written_local == buffer.used) {
         return F_none_eos;
       }
     }
 
-    if (F_status_is_error(status)) {
-      return F_status_set_error(status);
-    }
-
     return status;
   }
 #endif // _di_f_file_stream_write_
@@ -2503,7 +2508,7 @@ extern "C" {
     f_status_t status = F_none;
 
     if (written) {
-      private_f_file_stream_write_until(file, buffer, write_max, written);
+      status = private_f_file_stream_write_until(file, buffer, write_max, written);
 
       if (status == F_none) {
         if (*written == buffer.used) {
@@ -2518,7 +2523,7 @@ extern "C" {
     else {
       f_array_length_t written_local = 0;
 
-      private_f_file_stream_write_until(file, buffer, write_max, &written_local);
+      status = private_f_file_stream_write_until(file, buffer, write_max, &written_local);
 
       if (status == F_none) {
         if (written_local == buffer.used) {
@@ -2562,7 +2567,7 @@ extern "C" {
     f_status_t status = F_none;
 
     if (written) {
-      private_f_file_stream_write_until(file, buffer, write_max, written);
+      status = private_f_file_stream_write_until(file, buffer, write_max, written);
 
       if (status == F_none) {
         if (*written == buffer.used) {
@@ -2577,7 +2582,7 @@ extern "C" {
     else {
       f_array_length_t written_local = 0;
 
-      private_f_file_stream_write_until(file, buffer, buffer.used, &written_local);
+      status = private_f_file_stream_write_until(file, buffer, buffer.used, &written_local);
 
       if (status == F_none) {
         if (written_local == buffer.used) {
@@ -2624,11 +2629,7 @@ extern "C" {
     if (written) {
       const f_string_static_t buffer_adjusted = macro_f_string_static_t_initialize(buffer.string + range.start, 0, buffer.used - range.start);
 
-      flockfile(file.stream);
-
-      private_f_file_stream_write_until(file, buffer_adjusted, write_max, written);
-
-      funlockfile(file.stream);
+      status = private_f_file_stream_write_until(file, buffer_adjusted, write_max, written);
 
       if (status == F_none) {
         if (range.start + *written == buffer.used) {
@@ -2644,11 +2645,7 @@ extern "C" {
       const f_string_static_t buffer_adjusted = macro_f_string_static_t_initialize(buffer.string + range.start, 0, buffer.used - range.start);
       f_array_length_t written_local = 0;
 
-      flockfile(file.stream);
-
-      private_f_file_stream_write_until(file, buffer_adjusted, write_max, &written_local);
-
-      funlockfile(file.stream);
+      status = private_f_file_stream_write_until(file, buffer_adjusted, write_max, &written_local);
 
       if (status == F_none) {
         if (range.start + written_local == buffer.used) {
@@ -2677,7 +2674,7 @@ extern "C" {
 
     memset(&stat_file, 0, sizeof(struct stat));
 
-    status = private_f_file_stat(path, F_false, &stat_file);
+    status = private_f_file_stat(path, dereference, &stat_file);
 
     if (F_status_set_fine(status) == F_file_found_not) {
       return private_f_file_create(path, mode, dereference);
@@ -2748,7 +2745,7 @@ extern "C" {
 #endif // _di_f_file_touch_at_
 
 #ifndef _di_f_file_type_
-  f_status_t f_file_type(const f_string_static_t path, int * const type) {
+  f_status_t f_file_type(const f_string_static_t path, const bool dereference, int * const type) {
     #ifndef _di_level_0_parameter_checking_
       if (!type) return F_status_set_error(F_parameter);
     #endif // _di_level_0_parameter_checking_
@@ -2761,17 +2758,9 @@ extern "C" {
 
     memset(&stat_file, 0, sizeof(struct stat));
 
-    if (stat(path.string, &stat_file) < 0) {
-      if (errno == ENAMETOOLONG) return F_status_set_error(F_name);
-      if (errno == EFAULT) return F_status_set_error(F_buffer);
-      if (errno == ENOMEM) return F_status_set_error(F_memory_not);
-      if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow);
-      if (errno == ENOTDIR) return F_status_set_error(F_directory_not);
-      if (errno == ENOENT) return F_file_found_not;
-      if (errno == EACCES) return F_status_set_error(F_access_denied);
-      if (errno == ELOOP) return F_status_set_error(F_loop);
-
-      return F_status_set_error(F_file_stat);
+    {
+      const f_status_t status = private_f_file_stat(path, dereference, &stat_file);
+      if (F_status_is_error(status)) return status;
     }
 
     *type = macro_f_file_type_get(stat_file.st_mode);
@@ -2794,18 +2783,9 @@ extern "C" {
 
     memset(&stat_file, 0, sizeof(struct stat));
 
-    if (fstatat(at_id, path.string, &stat_file, flag) < 0) {
-      if (errno == EACCES) return F_status_set_error(F_access_denied);
-      if (errno == EBADF) return F_status_set_error(F_directory_descriptor);
-      if (errno == EFAULT) return F_status_set_error(F_buffer);
-      if (errno == ENAMETOOLONG) return F_status_set_error(F_name);
-      if (errno == ENOENT) return F_file_found_not;
-      if (errno == ENOMEM) return F_status_set_error(F_memory_not);
-      if (errno == ENOTDIR) return F_status_set_error(F_directory_not);
-      if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow);
-      if (errno == ELOOP) return F_status_set_error(F_loop);
-
-      return F_status_set_error(F_file_stat);
+    {
+      const f_status_t status = private_f_file_stat_at(at_id, path, flag, &stat_file);
+      if (F_status_is_error(status)) return status;
     }
 
     *type = macro_f_file_type_get(stat_file.st_mode);
index f5b08a34e5fea882a67f478cb78136430a9304a4..14319a4202005d7ef67084da0829b966094cdb0e 100644 (file)
@@ -2085,49 +2085,16 @@ extern "C" {
 #endif // _di_f_file_stream_close_
 
 /**
- * Open a file stream from a file descriptor.
- *
- * @param mode
- *   The file modes do use when opening.
- *   Set mode.used to 0 to determine mode from file.flags (falling back to read only as a failsafe).
- *   If neither truncate nor append are not specified in write only mode, then the failsafe is to append.
- *   This should match the modes used to open the file descriptor as it relates to the stream modes.
- * @param file
- *   The file with a valid file descriptor (file.id).
- *   THe file stream (file.stream) is updated on success, but may be set to NULL on error.
- *
- * @return
- *   F_none is returned on success.
- *
- *   F_access_denied (with error bit) on access denied.
- *   F_block (with error bit) if the action would block and non-blocking is set on the stream.
- *   F_buffer (with error bit) if the buffer is invalid.
- *   F_deadlock (with error bit) if operation would cause a deadlock.
- *   F_file_descriptor (with error bit) if file descriptor is invalid.
- *   F_file_descriptor_max (with error bit) if max file descriptors is reached.
- *   F_file_overflow (with error bit) if the write exceeds some implementation defined maximum file size.
- *   F_file_type_not_directory (with error bit) if F_NOTIFY was specified and file.id is not a directory.
- *   F_interrupt (with error bit) when program received an interrupt signal, halting operation.
- *   F_lock (with error bit) if failed to lock, such as lock table is full or too many open segments.
- *   F_parameter (with error bit) if a parameter is invalid.
- *   F_pipe_not (with error bit) if the stream is a pipe or a socket but the pipe or socket is already closed.
- *   F_prohibited (with error bit) if file system does not allow for making changes.
- *   F_socket_not (with error bit) if socket is not connected.
- *   F_space_not (with error bit) if the file system is out of space (or file system quota is reached).
- *
- * @see fdopen()
- */
-#ifndef _di_f_file_stream_open_descriptor_
-  extern f_status_t f_file_stream_open_descriptor(const f_string_static_t mode, f_file_t * const file);
-#endif // _di_f_file_stream_open_descriptor_
-
-/**
  * Open a file stream.
  *
  * The file descriptor is retrieved on success, if necessary and able.
  *
+ * If the file stream is open, it is closed before re-opening.
+ *
+ * This is often used for changing the file pointed to by standard streams such as stdout.
+ *
  * @param path
- *   The file path
+ *   The file path.
  * @param mode
  *   The file modes do use when opening, as an fopen() file mode string.
  *   Set mode.used to 0 to determine mode from file.flags (falling back to read only as a failsafe).
@@ -2147,6 +2114,7 @@ extern "C" {
  *
  * @return
  *   F_none is returned on success.
+ *   F_data_not if both path.used is 0.
  *
  *   F_access_denied (with error bit) on access denied.
  *   F_buffer (with error bit) if the buffer is invalid.
@@ -2175,6 +2143,43 @@ extern "C" {
 #endif // _di_f_file_stream_open_
 
 /**
+ * Open a file stream from a file descriptor.
+ *
+ * @param mode
+ *   The file modes do use when opening.
+ *   Set mode.used to 0 to determine mode from file.flags (falling back to read only as a failsafe).
+ *   If neither truncate nor append are not specified in write only mode, then the failsafe is to append.
+ *   This should match the modes used to open the file descriptor as it relates to the stream modes.
+ * @param file
+ *   The file with a valid file descriptor (file.id).
+ *   THe file stream (file.stream) is updated on success, but may be set to NULL on error.
+ *
+ * @return
+ *   F_none is returned on success.
+ *
+ *   F_access_denied (with error bit) on access denied.
+ *   F_block (with error bit) if the action would block and non-blocking is set on the stream.
+ *   F_buffer (with error bit) if the buffer is invalid.
+ *   F_deadlock (with error bit) if operation would cause a deadlock.
+ *   F_file_descriptor (with error bit) if file descriptor is invalid.
+ *   F_file_descriptor_max (with error bit) if max file descriptors is reached.
+ *   F_file_overflow (with error bit) if the write exceeds some implementation defined maximum file size.
+ *   F_file_type_not_directory (with error bit) if F_NOTIFY was specified and file.id is not a directory.
+ *   F_interrupt (with error bit) when program received an interrupt signal, halting operation.
+ *   F_lock (with error bit) if failed to lock, such as lock table is full or too many open segments.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_pipe_not (with error bit) if the stream is a pipe or a socket but the pipe or socket is already closed.
+ *   F_prohibited (with error bit) if file system does not allow for making changes.
+ *   F_socket_not (with error bit) if socket is not connected.
+ *   F_space_not (with error bit) if the file system is out of space (or file system quota is reached).
+ *
+ * @see fdopen()
+ */
+#ifndef _di_f_file_stream_open_descriptor_
+  extern f_status_t f_file_stream_open_descriptor(const f_string_static_t mode, f_file_t * const file);
+#endif // _di_f_file_stream_open_descriptor_
+
+/**
  * Read until EOF is reached.
  *
  * To check how much was read into the buffer, record buffer->used before execution and compare to buffer->used after execution.
@@ -2294,7 +2299,8 @@ extern "C" {
  * The file descriptor is retrieved on success, if necessary and able.
  *
  * @param path
- *   The file path
+ *   The file path.
+ *   Set path.used to 0 with a non-empty mode (mode.used > 0) to only change the mode of the existing stream.
  * @param mode
  *   The file modes do use when opening.
  *   Set to 0 to determine mode from file.flags (falling back to read only as a failsafe).
@@ -2306,6 +2312,7 @@ extern "C" {
  *
  * @return
  *   F_none is returned on success.
+ *   F_data_not if both path.used and mode.used are 0.
  *
  *   F_access_denied (with error bit) on access denied.
  *   F_buffer (with error bit) if the buffer is invalid.
@@ -2360,7 +2367,12 @@ extern "C" {
  *   F_interrupt (with error bit) if interrupt was received.
  *   F_parameter (with error bit) if a parameter is invalid.
  *
- * @see fwrite()
+ *   F_file_write (with error bit) on any other error.
+ *
+ * @see flockfile()
+ * @see fwrite_unlocked()
+ * @see ferror_unlocked()
+ * @see funlockfile()
  */
 #ifndef _di_f_file_stream_write_
   extern f_status_t f_file_stream_write(const f_file_t file, const f_string_static_t buffer, f_array_length_t * const written);
@@ -2395,7 +2407,12 @@ extern "C" {
  *   F_interrupt (with error bit) if interrupt was received.
  *   F_parameter (with error bit) if a parameter is invalid.
  *
- * @see fwrite()
+ *   F_file_write (with error bit) on any other error.
+ *
+ * @see flockfile()
+ * @see fwrite_unlocked()
+ * @see ferror_unlocked()
+ * @see funlockfile()
  */
 #ifndef _di_f_file_stream_write_block_
   extern f_status_t f_file_stream_write_block(const f_file_t file, const f_string_static_t buffer, f_array_length_t * const written);
@@ -2431,7 +2448,12 @@ extern "C" {
  *   F_interrupt (with error bit) if interrupt was received.
  *   F_parameter (with error bit) if a parameter is invalid.
  *
- * @see fwrite()
+ *   F_file_write (with error bit) on any other error.
+ *
+ * @see flockfile()
+ * @see fwrite_unlocked()
+ * @see ferror_unlocked()
+ * @see funlockfile()
  */
 #ifndef _di_f_file_stream_write_until_
   extern f_status_t f_file_stream_write_until(const f_file_t file, const f_string_static_t buffer, const f_array_length_t total, f_array_length_t * const written);
@@ -2565,6 +2587,9 @@ extern "C" {
  *
  * @param path
  *   The path file name.
+ * @param dereference
+ *   Set to TRUE to dereference symlinks (often is what is desired).
+ *   Set to FALSE to operate on the symlink itself.
  * @param type
  *   The type of the file.
  *
@@ -2583,7 +2608,7 @@ extern "C" {
  * @see stat()
  */
 #ifndef _di_f_file_type_
-  extern f_status_t f_file_type(const f_string_static_t path, int * const type);
+  extern f_status_t f_file_type(const f_string_static_t path, const bool dereference, int * const type);
 #endif //  _di_f_file_type_
 
 /**
index 5f69be9578054e946a21a6ea781fe1358f58e595..d2af685d2f509fbbbdebe514a588e859434eb139 100644 (file)
@@ -787,11 +787,15 @@ extern "C" {
       }
     }
 
+    flockfile(file.stream);
+
     while (*written < write_max) {
 
       size_write = fwrite_unlocked(buffer.string + *written, write_amount, write_size, file.stream);
 
-      if (size_write < 0) {
+      if (ferror_unlocked(file.stream)) {
+        funlockfile(file.stream);
+
         if (errno == EAGAIN || errno == EWOULDBLOCK) return F_status_set_error(F_block);
         if (errno == EBADF) return F_status_set_error(F_file_descriptor);
         if (errno == EFAULT) return F_status_set_error(F_buffer);
@@ -800,16 +804,20 @@ extern "C" {
         if (errno == EIO) return F_status_set_error(F_input_output);
         if (errno == EISDIR) return F_status_set_error(F_file_type_directory);
 
-        return F_status_set_error(F_failure);
+        return F_status_set_error(F_file_write);
       }
 
       *written += size_write * write_amount;
 
       if (!size_write) {
+        funlockfile(file.stream);
+
         return F_none_stop;
       }
     } // while
 
+    funlockfile(file.stream);
+
     return F_none;
   }
 #endif // !defined(f_file_stream_write) || !defined(_di_f_file_stream_write_block_) || !defined(f_file_stream_write_until) || !defined(f_file_stream_write_range)
@@ -850,7 +858,7 @@ extern "C" {
       if (errno == EIO) return F_status_set_error(F_input_output);
       if (errno == EISDIR) return F_status_set_error(F_file_type_directory);
 
-      return F_status_set_error(F_failure);
+      return F_status_set_error(F_file_write);
     }
 
     return F_none;