filesetup: fix win raw disk access and improve dir creation failure msg
authorSitsofe Wheeler <sitsofe@yahoo.com>
Sun, 1 Mar 2020 18:27:19 +0000 (18:27 +0000)
committerSitsofe Wheeler <sitsofe@yahoo.com>
Sun, 1 Mar 2020 22:12:27 +0000 (22:12 +0000)
The commit df18600fd06258b96ae6f6b530ecdff541c2a82d ("filesetup: fix
directory creation issues") broke Windows raw/physical disk access
because Windows doesn't consider a path that only consists of a
namespace (such as the device namespace "\\.\" - see
https://docs.microsoft.com/en-gb/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#namespaces
for information on Windows' namespaces) to exist as a directory(!).

Workaround the issue for raw devices by explicitly considering the
device namespace subcomponent to always be valid. Further, intermediate
path components in UNC paths or paths starting with a namespace might
also not "exist" so introduce backwards scanning for the longest
pre-existing directory to sidestep this. The function doing this is made
available for non-windows platforms so a similar code path is used
everywhere.

Tests done:

Windows:
> ./fio.exe --name=dtest --thread --size=16k --rw=write `
  --filename 'fio.tmp'
> ./fio.exe --name=dtest --thread --size=16k --rw=write `
  --filename '\\?\C\:\Windows\Temp\fio\fio.tmp'
> Clear-Disk 1 -RemoveData -Confirm:$false # Destroys partition data!
> ./fio.exe --name=dtest --thread --size=16k --rw=write `
   --filename '\\.\PhysicalDrive1'
> ./fio.exe --name=dtest --thread --size=16k --rw=write `
  --filename '\fio.tmp'
> ./fio.exe --name=dtest --thread --size=16k --rw=write `
  --filename '\\LOCALHOST\Users\User\fio\fio.tmp'

macOS:
$ rm -rf /tmp/fio
$ ./fio --name=dtest --size=16k --filename /tmp/fio/fio.tmp \
  --rw=write

Finally, change the directory creation error message to give a human
error message rather than just an errno.

Fixes: https://github.com/axboe/fio/issues/916
Signed-off-by: Sitsofe Wheeler <sitsofe@yahoo.com>
filesetup.c
os/os-windows.h

index b45a58265a140700bfb96b4e6dd0930e4ed1a6c2..8a4091fcc25420325f6bb1779dfffa0853f8ee2e 100644 (file)
@@ -913,15 +913,61 @@ uint64_t get_start_offset(struct thread_data *td, struct fio_file *f)
        return offset;
 }
 
+/*
+ * Find longest path component that exists and return its length
+ */
+int longest_existing_path(char *path) {
+       char buf[PATH_MAX];
+       bool done;
+       char *buf_pos;
+       int offset;
+#ifdef WIN32
+       DWORD dwAttr;
+#else
+       struct stat sb;
+#endif
+
+       sprintf(buf, "%s", path);
+       done = false;
+       while (!done) {
+               buf_pos = strrchr(buf, FIO_OS_PATH_SEPARATOR);
+               if (!buf_pos) {
+                       done = true;
+                       offset = 0;
+                       break;
+               }
+
+               *(buf_pos + 1) = '\0';
+
+#ifdef WIN32
+               dwAttr = GetFileAttributesA(buf);
+               if (dwAttr != INVALID_FILE_ATTRIBUTES) {
+                       done = true;
+               }
+#else
+               if (stat(buf, &sb) == 0)
+                       done = true;
+#endif
+               if (done)
+                       offset = buf_pos - buf;
+               else
+                       *buf_pos = '\0';
+       }
+
+       return offset;
+}
+
 static bool create_work_dirs(struct thread_data *td, const char *fname)
 {
        char path[PATH_MAX];
        char *start, *end;
+       int offset;
 
        snprintf(path, PATH_MAX, "%s", fname);
        start = path;
 
-       end = start;
+       offset = longest_existing_path(path);
+       end = start + offset;
        while ((end = strchr(end, FIO_OS_PATH_SEPARATOR)) != NULL) {
                if (end == start) {
                        end++;
@@ -930,8 +976,8 @@ static bool create_work_dirs(struct thread_data *td, const char *fname)
                *end = '\0';
                errno = 0;
                if (fio_mkdir(path, 0700) && errno != EEXIST) {
-                       log_err("fio: failed to create dir (%s): %d\n",
-                               start, errno);
+                       log_err("fio: failed to create dir (%s): %s\n",
+                               start, strerror(errno));
                        return false;
                }
                *end = FIO_OS_PATH_SEPARATOR;
index 6d48ffe81819e9e6bd91476acc80c32525aa3b7a..fa2955f98659c84a539e26268bdb5a6673fb31ee 100644 (file)
@@ -203,7 +203,11 @@ static inline int fio_mkdir(const char *path, mode_t mode) {
        }
 
        if (CreateDirectoryA(path, NULL) == 0) {
-               log_err("CreateDirectoryA = %d\n", GetLastError());
+               /* Ignore errors if path is a device namespace */
+               if (strcmp(path, "\\\\.") == 0) {
+                       errno = EEXIST;
+                       return -1;
+               }
                errno = win_to_posix_error(GetLastError());
                return -1;
        }