source: trunk/libtransmission/file-win32.c

Last change on this file was 14588, checked in by mikedld, 5 years ago

Support absolute paths longer than ~260 chars on Windows

  • Property svn:keywords set to Date Rev Author Id
File size: 30.0 KB
Line 
1/*
2 * This file Copyright (C) 2013-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: file-win32.c 14588 2015-10-23 05:29:47Z mikedld $
8 */
9
10#include <assert.h>
11#include <ctype.h> /* isalpha () */
12
13#include <shlobj.h> /* SHCreateDirectoryEx () */
14#include <winioctl.h> /* FSCTL_SET_SPARSE */
15
16#include "transmission.h"
17#include "crypto-utils.h" /* tr_rand_int () */
18#include "error.h"
19#include "file.h"
20#include "utils.h"
21
22#ifndef MAXSIZE_T
23 #define MAXSIZE_T ((SIZE_T)~((SIZE_T)0))
24#endif
25
26/* MSDN (http://msdn.microsoft.com/en-us/library/2k2xf226.aspx) only mentions
27   "i64" suffix for C code, but no warning is issued */
28#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
29
30struct tr_sys_dir_win32
31{
32  wchar_t * pattern;
33  HANDLE find_handle;
34  WIN32_FIND_DATAW find_data;
35  char * utf8_name;
36};
37
38static void
39set_system_error (tr_error ** error,
40                  DWORD       code)
41{
42  char * message;
43
44  if (error == NULL)
45    return;
46
47  message = tr_win32_format_message (code);
48
49  if (message != NULL)
50    {
51      tr_error_set_literal (error, code, message);
52      tr_free (message);
53    }
54  else
55    {
56      tr_error_set (error, code, "Unknown error: 0x%08lx", code);
57    }
58}
59
60static void
61set_system_error_if_file_found (tr_error ** error,
62                                DWORD       code)
63{
64  if (code != ERROR_FILE_NOT_FOUND &&
65      code != ERROR_PATH_NOT_FOUND &&
66      code != ERROR_NO_MORE_FILES)
67    set_system_error (error, code);
68}
69
70static time_t
71filetime_to_unix_time (const FILETIME * t)
72{
73  uint64_t tmp = 0;
74
75  assert (t != NULL);
76
77  tmp |= t->dwHighDateTime;
78  tmp <<= 32;
79  tmp |= t->dwLowDateTime;
80  tmp /= 10; /* to microseconds */
81  tmp -= DELTA_EPOCH_IN_MICROSECS;
82
83  return tmp / 1000000UL;
84}
85
86static void
87stat_to_sys_path_info (DWORD              attributes,
88                       DWORD              size_low,
89                       DWORD              size_high,
90                       const FILETIME   * mtime,
91                       tr_sys_path_info * info)
92{
93  assert (mtime != NULL);
94  assert (info != NULL);
95
96  if (attributes & FILE_ATTRIBUTE_DIRECTORY)
97    info->type = TR_SYS_PATH_IS_DIRECTORY;
98  else if (!(attributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_VIRTUAL)))
99    info->type = TR_SYS_PATH_IS_FILE;
100  else
101    info->type = TR_SYS_PATH_IS_OTHER;
102
103  info->size = size_high;
104  info->size <<= 32;
105  info->size |= size_low;
106
107  info->last_modified_at = filetime_to_unix_time (mtime);
108}
109
110static inline bool
111is_slash (char c)
112{
113  return c == '\\' || c == '/';
114}
115
116static inline bool
117is_unc_path (const char * path)
118{
119  return is_slash (path[0]) && path[1] == path[0];
120}
121
122static bool
123is_valid_path (const char * path)
124{
125  if (is_unc_path (path))
126    {
127      if (path[2] != '\0' && !isalnum (path[2]))
128        return false;
129    }
130  else
131    {
132      const char * colon_pos = strchr (path, ':');
133      if (colon_pos != NULL)
134        {
135          if (colon_pos != path + 1 || !isalpha (path[0]))
136            return false;
137          path += 2;
138        }
139    }
140
141  return strpbrk (path, "<>:\"|?*") == NULL;
142}
143
144static wchar_t *
145path_to_native_path_ex (const char * path,
146                        int          extra_chars_after,
147                        int        * real_result_size)
148{
149  /* Extending maximum path length limit up to ~32K. See "Naming Files, Paths, and Namespaces"
150     (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx) for more info */
151
152  const wchar_t local_prefix[] = { '\\', '\\', '?', '\\' };
153  const wchar_t unc_prefix[] = { '\\', '\\', '?', '\\', 'U', 'N', 'C', '\\' };
154
155  const bool is_relative = tr_sys_path_is_relative (path);
156  const bool is_unc = is_unc_path (path);
157
158  /* `-2` for UNC since we overwrite existing prefix slashes */
159  const int extra_chars_before = is_relative ? 0 : (is_unc ? ARRAYSIZE (unc_prefix) - 2
160                                                           : ARRAYSIZE (local_prefix));
161
162  /* TODO (?): assert (!is_relative); */
163
164  wchar_t * const wide_path = tr_win32_utf8_to_native_ex (path, -1, extra_chars_before,
165                                                          extra_chars_after, real_result_size);
166  if (wide_path == NULL)
167    return NULL;
168
169  /* Relative paths cannot be used with "\\?\" prefixes. This also means that relative paths are
170     limited to ~260 chars... but we should rarely work with relative paths in the first place */
171  if (!is_relative)
172    {
173      if (is_unc)
174        /* UNC path: "\\server\share" -> "\\?\UNC\server\share" */
175        memcpy (wide_path, unc_prefix, sizeof (unc_prefix));
176      else
177        /* Local path: "C:" -> "\\?\C:" */
178        memcpy (wide_path, local_prefix, sizeof (local_prefix));
179    }
180
181  /* Automatic '/' to '\' conversion is disabled for "\\?\"-prefixed paths */
182  wchar_t * p = wide_path + extra_chars_before;
183  while ((p = wcschr (p, L'/')) != NULL)
184    *p++ = L'\\';
185
186  if (real_result_size != NULL)
187    *real_result_size += extra_chars_before;
188
189  return wide_path;
190}
191
192static wchar_t *
193path_to_native_path (const char * path)
194{
195  return path_to_native_path_ex (path, 0, NULL);
196}
197
198static tr_sys_file_t
199open_file (const char  * path,
200           DWORD         access,
201           DWORD         disposition,
202           DWORD         flags,
203           tr_error   ** error)
204{
205  tr_sys_file_t ret = TR_BAD_SYS_FILE;
206  wchar_t * wide_path;
207
208  assert (path != NULL);
209
210  wide_path = path_to_native_path (path);
211
212  if (wide_path != NULL)
213    ret = CreateFileW (wide_path, access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
214                       NULL, disposition, flags, NULL);
215
216  if (ret == TR_BAD_SYS_FILE)
217    set_system_error (error, GetLastError ());
218
219  tr_free (wide_path);
220
221  return ret;
222}
223
224static bool
225create_dir (const char  * path,
226            int           flags,
227            int           permissions,
228            bool          okay_if_exists,
229            tr_error   ** error)
230{
231  bool ret;
232  wchar_t * wide_path;
233  DWORD error_code = ERROR_SUCCESS;
234
235  assert (path != NULL);
236
237  (void) permissions;
238
239  wide_path = path_to_native_path (path);
240
241  if ((flags & TR_SYS_DIR_CREATE_PARENTS) != 0)
242    {
243      error_code = SHCreateDirectoryExW (NULL, wide_path, NULL);
244      ret = error_code == ERROR_SUCCESS;
245    }
246  else
247    {
248      ret = CreateDirectoryW (wide_path, NULL);
249      if (!ret)
250        error_code = GetLastError ();
251    }
252
253  if (!ret && error_code == ERROR_ALREADY_EXISTS && okay_if_exists)
254    {
255      const DWORD attributes = GetFileAttributesW (wide_path);
256      if (attributes != INVALID_FILE_ATTRIBUTES &&
257          (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
258        ret = true;
259    }
260
261  if (!ret)
262    set_system_error (error, error_code);
263
264  tr_free (wide_path);
265
266  return ret;
267}
268
269static void
270create_temp_path (char      * path_template,
271                  void     (* callback) (const char * path, void * param, tr_error ** error),
272                  void      * callback_param,
273                  tr_error ** error)
274{
275  char * path;
276  size_t path_size;
277  int attempt;
278  tr_error * my_error = NULL;
279
280  assert (path_template != NULL);
281  assert (callback != NULL);
282
283  path = tr_strdup (path_template);
284  path_size = strlen (path);
285
286  assert (path_size > 0);
287
288  for (attempt = 0; attempt < 100; ++attempt)
289    {
290      size_t i = path_size;
291
292      while (i > 0 && path_template[i - 1] == 'X')
293        {
294          const int c = tr_rand_int (26 + 26 + 10);
295          path[i - 1] = c < 26 ? c + 'A' : (c < 26 + 26 ? (c - 26) + 'a' : (c - 26 - 26) + '0');
296          --i;
297        }
298
299      assert (path_size >= i + 6);
300
301      tr_error_clear (&my_error);
302
303      (*callback) (path, callback_param, &my_error);
304
305      if (my_error == NULL)
306        break;
307    }
308
309  if (my_error != NULL)
310    tr_error_propagate(error, &my_error);
311  else
312    memcpy (path_template, path, path_size);
313
314  tr_free (path);
315}
316
317bool
318tr_sys_path_exists (const char  * path,
319                    tr_error   ** error)
320{
321  bool ret = false;
322  wchar_t * wide_path;
323  HANDLE handle = INVALID_HANDLE_VALUE;
324
325  assert (path != NULL);
326
327  wide_path = path_to_native_path (path);
328
329  if (wide_path != NULL)
330    {
331      DWORD attributes = GetFileAttributesW (wide_path);
332      if (attributes != INVALID_FILE_ATTRIBUTES)
333        {
334          if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
335            {
336              handle = CreateFileW (wide_path, 0, 0, NULL, OPEN_EXISTING,
337                                    FILE_FLAG_BACKUP_SEMANTICS, NULL);
338
339              ret = handle != INVALID_HANDLE_VALUE;
340            }
341          else
342            {
343              ret = true;
344            }
345        }
346    }
347
348  if (!ret)
349    set_system_error_if_file_found (error, GetLastError ());
350
351  if (handle != INVALID_HANDLE_VALUE)
352    CloseHandle (handle);
353
354  tr_free (wide_path);
355
356  return ret;
357}
358
359bool
360tr_sys_path_get_info (const char        * path,
361                      int                 flags,
362                      tr_sys_path_info  * info,
363                      tr_error         ** error)
364{
365  bool ret = false;
366  wchar_t * wide_path;
367
368  assert (path != NULL);
369  assert (info != NULL);
370
371  wide_path = path_to_native_path (path);
372
373  if ((flags & TR_SYS_PATH_NO_FOLLOW) == 0)
374    {
375      HANDLE handle = INVALID_HANDLE_VALUE;
376
377      if (wide_path != NULL)
378        handle = CreateFileW (wide_path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
379
380      if (handle != INVALID_HANDLE_VALUE)
381        {
382          tr_error * my_error = NULL;
383          ret = tr_sys_file_get_info (handle, info, &my_error);
384          if (!ret)
385            tr_error_propagate (error, &my_error);
386          CloseHandle (handle);
387        }
388      else
389        {
390          set_system_error (error, GetLastError ());
391        }
392    }
393  else
394    {
395      WIN32_FILE_ATTRIBUTE_DATA attributes;
396
397      if (wide_path != NULL)
398        ret = GetFileAttributesExW (wide_path, GetFileExInfoStandard, &attributes);
399
400      if (ret)
401        stat_to_sys_path_info (attributes.dwFileAttributes, attributes.nFileSizeLow,
402                               attributes.nFileSizeHigh, &attributes.ftLastWriteTime,
403                               info);
404      else
405        set_system_error (error, GetLastError ());
406    }
407
408  tr_free (wide_path);
409
410  return ret;
411}
412
413bool
414tr_sys_path_is_relative (const char * path)
415{
416  assert (path != NULL);
417
418  /* UNC path: `\\...`. */
419  if (is_unc_path (path))
420    return false;
421
422  /* Local path: `X:` or `X:\...`. */
423  if (isalpha (path[0]) && path[1] == ':' && (path[2] == '\0' || is_slash (path[2])))
424    return false;
425
426  return true;
427}
428
429bool
430tr_sys_path_is_same (const char  * path1,
431                     const char  * path2,
432                     tr_error   ** error)
433{
434  bool ret = false;
435  wchar_t * wide_path1 = NULL;
436  wchar_t * wide_path2 = NULL;
437  HANDLE handle1 = INVALID_HANDLE_VALUE;
438  HANDLE handle2 = INVALID_HANDLE_VALUE;
439  BY_HANDLE_FILE_INFORMATION fi1, fi2;
440
441  assert (path1 != NULL);
442  assert (path2 != NULL);
443
444  wide_path1 = path_to_native_path (path1);
445  if (wide_path1 == NULL)
446    goto fail;
447
448  wide_path2 = path_to_native_path (path2);
449  if (wide_path2 == NULL)
450    goto fail;
451
452  handle1 = CreateFileW (wide_path1, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
453  if (handle1 == INVALID_HANDLE_VALUE)
454    goto fail;
455
456  handle2 = CreateFileW (wide_path2, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
457  if (handle2 == INVALID_HANDLE_VALUE)
458    goto fail;
459
460  /* TODO: Use GetFileInformationByHandleEx on >= Server 2012 */
461
462  if (!GetFileInformationByHandle (handle1, &fi1) || !GetFileInformationByHandle (handle2, &fi2))
463    goto fail;
464
465  ret = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
466        fi1.nFileIndexHigh == fi2.nFileIndexHigh &&
467        fi1.nFileIndexLow  == fi2.nFileIndexLow;
468
469  goto cleanup;
470
471fail:
472  set_system_error_if_file_found (error, GetLastError ());
473
474cleanup:
475  CloseHandle (handle2);
476  CloseHandle (handle1);
477
478  tr_free (wide_path2);
479  tr_free (wide_path1);
480
481  return ret;
482}
483
484char *
485tr_sys_path_resolve (const char  * path,
486                     tr_error   ** error)
487{
488  char * ret = NULL;
489  wchar_t * wide_path;
490  wchar_t * wide_ret = NULL;
491  HANDLE handle = INVALID_HANDLE_VALUE;
492  DWORD wide_ret_size;
493
494  assert (path != NULL);
495
496  wide_path = path_to_native_path (path);
497  if (wide_path == NULL)
498    goto fail;
499
500  handle = CreateFileW (wide_path, FILE_READ_EA,
501                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
502                        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
503  if (handle == INVALID_HANDLE_VALUE)
504    goto fail;
505
506  wide_ret_size = GetFinalPathNameByHandleW (handle, NULL, 0, 0);
507  if (wide_ret_size == 0)
508    goto fail;
509
510  wide_ret = tr_new (wchar_t, wide_ret_size);
511  if (GetFinalPathNameByHandleW (handle, wide_ret, wide_ret_size, 0) != wide_ret_size - 1)
512    goto fail;
513
514  /* Resolved path always begins with "\\?\", so skip those first four chars. */
515  ret = tr_win32_native_to_utf8 (wide_ret + 4, -1);
516  if (ret != NULL)
517    goto cleanup;
518
519fail:
520  set_system_error (error, GetLastError ());
521
522  tr_free (ret);
523  ret = NULL;
524
525cleanup:
526  tr_free (wide_ret);
527  tr_free (wide_path);
528
529  if (handle != INVALID_HANDLE_VALUE)
530    CloseHandle (handle);
531
532  return ret;
533}
534
535char *
536tr_sys_path_basename (const char  * path,
537                      tr_error   ** error)
538{
539  if (path == NULL || path[0] == '\0')
540    return tr_strdup (".");
541
542  if (!is_valid_path (path))
543    {
544      set_system_error (error, ERROR_PATH_NOT_FOUND);
545      return NULL;
546    }
547
548  const char * end = path + strlen (path);
549  while (end > path && is_slash (*(end - 1)))
550    --end;
551
552  if (end == path)
553    return tr_strdup ("/");
554
555  const char * name = end;
556  while (name > path && *(name - 1) != ':' && !is_slash (*(name - 1)))
557    --name;
558
559  if (name == end)
560    return tr_strdup ("/");
561
562  return tr_strndup (name, end - name);
563}
564
565char *
566tr_sys_path_dirname (const char  * path,
567                     tr_error   ** error)
568{
569  if (path == NULL || path[0] == '\0')
570    return tr_strdup (".");
571
572  if (!is_valid_path (path))
573    {
574      set_system_error (error, ERROR_PATH_NOT_FOUND);
575      return NULL;
576    }
577
578  const bool is_unc = is_unc_path (path);
579
580  if (is_unc && path[2] == '\0')
581    return tr_strdup (path);
582
583  const char * end = path + strlen (path);
584  while (end > path && is_slash (*(end - 1)))
585    --end;
586
587  if (end == path)
588    return tr_strdup ("/");
589
590  const char * name = end;
591  while (name > path && *(name - 1) != ':' && !is_slash (*(name - 1)))
592    --name;
593  while (name > path && is_slash (*(name - 1)))
594    --name;
595
596  if (name == path)
597    return tr_strdup (is_unc ? "\\\\" : ".");
598
599  if (name > path && *(name - 1) == ':' && *name != '\0' && !is_slash (*name))
600    return tr_strdup_printf ("%c:.", path[0]);
601
602  return tr_strndup (path, name - path);
603}
604
605bool
606tr_sys_path_rename (const char  * src_path,
607                    const char  * dst_path,
608                    tr_error   ** error)
609{
610  bool ret = false;
611  wchar_t * wide_src_path;
612  wchar_t * wide_dst_path;
613
614  assert (src_path != NULL);
615  assert (dst_path != NULL);
616
617  wide_src_path = path_to_native_path (src_path);
618  wide_dst_path = path_to_native_path (dst_path);
619
620  if (wide_src_path != NULL && wide_dst_path != NULL)
621    {
622      DWORD flags = MOVEFILE_REPLACE_EXISTING;
623      DWORD attributes;
624
625      attributes = GetFileAttributesW (wide_src_path);
626      if (attributes != INVALID_FILE_ATTRIBUTES &&
627          (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
628        {
629          flags = 0;
630        }
631      else
632        {
633          attributes = GetFileAttributesW (wide_dst_path);
634          if (attributes != INVALID_FILE_ATTRIBUTES &&
635              (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
636            flags = 0;
637        }
638
639      ret = MoveFileExW (wide_src_path, wide_dst_path, flags);
640    }
641
642  if (!ret)
643    set_system_error (error, GetLastError ());
644
645  tr_free (wide_dst_path);
646  tr_free (wide_src_path);
647
648  return ret;
649}
650
651bool
652tr_sys_path_remove (const char  * path,
653                    tr_error   ** error)
654{
655  bool ret = false;
656  wchar_t * wide_path;
657
658  assert (path != NULL);
659
660  wide_path = path_to_native_path (path);
661
662  if (wide_path != NULL)
663    {
664      const DWORD attributes = GetFileAttributesW (wide_path);
665
666      if (attributes != INVALID_FILE_ATTRIBUTES)
667        {
668          if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
669            ret = RemoveDirectoryW (wide_path);
670          else
671            ret = DeleteFileW (wide_path);
672        }
673    }
674
675  if (!ret)
676    set_system_error (error, GetLastError ());
677
678  tr_free (wide_path);
679
680  return ret;
681}
682
683tr_sys_file_t
684tr_sys_file_get_std (tr_std_sys_file_t    std_file,
685                     tr_error          ** error)
686{
687  tr_sys_file_t ret = TR_BAD_SYS_FILE;
688
689  switch (std_file)
690    {
691    case TR_STD_SYS_FILE_IN:
692      ret = GetStdHandle (STD_INPUT_HANDLE);
693      break;
694    case TR_STD_SYS_FILE_OUT:
695      ret = GetStdHandle (STD_OUTPUT_HANDLE);
696      break;
697    case TR_STD_SYS_FILE_ERR:
698      ret = GetStdHandle (STD_ERROR_HANDLE);
699      break;
700    default:
701      assert (0 && "Unknown standard file");
702      set_system_error (error, ERROR_INVALID_PARAMETER);
703      return TR_BAD_SYS_FILE;
704    }
705
706  if (ret == TR_BAD_SYS_FILE)
707    set_system_error (error, GetLastError ());
708  else if (ret == NULL)
709    ret = TR_BAD_SYS_FILE;
710
711  return ret;
712}
713
714tr_sys_file_t
715tr_sys_file_open (const char  * path,
716                  int           flags,
717                  int           permissions,
718                  tr_error   ** error)
719{
720  tr_sys_file_t ret;
721  DWORD native_access = 0;
722  DWORD native_disposition = OPEN_EXISTING;
723  DWORD native_flags = FILE_ATTRIBUTE_NORMAL;
724  bool success;
725
726  assert (path != NULL);
727  assert ((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) != 0);
728
729  (void) permissions;
730
731  if (flags & TR_SYS_FILE_READ)
732    native_access |= GENERIC_READ;
733  if (flags & TR_SYS_FILE_WRITE)
734    native_access |= GENERIC_WRITE;
735
736  if (flags & TR_SYS_FILE_CREATE_NEW)
737    native_disposition = CREATE_NEW;
738  else if (flags & TR_SYS_FILE_CREATE)
739    native_disposition = flags & TR_SYS_FILE_TRUNCATE ? CREATE_ALWAYS : OPEN_ALWAYS;
740  else if (flags & TR_SYS_FILE_TRUNCATE)
741    native_disposition = TRUNCATE_EXISTING;
742
743  if (flags & TR_SYS_FILE_SEQUENTIAL)
744    native_flags |= FILE_FLAG_SEQUENTIAL_SCAN;
745
746  ret = open_file (path, native_access, native_disposition, native_flags, error);
747
748  success = ret != TR_BAD_SYS_FILE;
749
750  if (success && (flags & TR_SYS_FILE_APPEND))
751    success = SetFilePointer (ret, 0, NULL, FILE_END) != INVALID_SET_FILE_POINTER;
752
753  if (!success)
754    {
755      if (error == NULL)
756        set_system_error (error, GetLastError ());
757
758      CloseHandle (ret);
759      ret = TR_BAD_SYS_FILE;
760    }
761
762  return ret;
763}
764
765static void
766file_open_temp_callback (const char  * path,
767                         void        * param,
768                         tr_error   ** error)
769{
770  tr_sys_file_t * result = (tr_sys_file_t *) param;
771
772  assert (result != NULL);
773
774  *result = open_file (path,
775                       GENERIC_READ | GENERIC_WRITE,
776                       CREATE_NEW,
777                       FILE_ATTRIBUTE_TEMPORARY,
778                       error);
779}
780
781tr_sys_file_t
782tr_sys_file_open_temp (char      * path_template,
783                       tr_error ** error)
784{
785  tr_sys_file_t ret = TR_BAD_SYS_FILE;
786
787  assert (path_template != NULL);
788
789  create_temp_path (path_template, file_open_temp_callback, &ret, error);
790
791  return ret;
792}
793
794bool
795tr_sys_file_close (tr_sys_file_t    handle,
796                   tr_error      ** error)
797{
798  bool ret;
799
800  assert (handle != TR_BAD_SYS_FILE);
801
802  ret = CloseHandle (handle);
803
804  if (!ret)
805    set_system_error (error, GetLastError ());
806
807  return ret;
808}
809
810bool
811tr_sys_file_get_info (tr_sys_file_t       handle,
812                      tr_sys_path_info  * info,
813                      tr_error         ** error)
814{
815  bool ret;
816  BY_HANDLE_FILE_INFORMATION attributes;
817
818  assert (handle != TR_BAD_SYS_FILE);
819  assert (info != NULL);
820
821  ret = GetFileInformationByHandle (handle, &attributes);
822
823  if (ret)
824    stat_to_sys_path_info (attributes.dwFileAttributes, attributes.nFileSizeLow,
825                           attributes.nFileSizeHigh, &attributes.ftLastWriteTime,
826                           info);
827  else
828    set_system_error (error, GetLastError ());
829
830  return ret;
831}
832
833bool
834tr_sys_file_seek (tr_sys_file_t       handle,
835                  int64_t             offset,
836                  tr_seek_origin_t    origin,
837                  uint64_t          * new_offset,
838                  tr_error         ** error)
839{
840  bool ret = false;
841  LARGE_INTEGER native_offset, new_native_pointer;
842
843  TR_STATIC_ASSERT (TR_SEEK_SET == FILE_BEGIN,   "values should match");
844  TR_STATIC_ASSERT (TR_SEEK_CUR == FILE_CURRENT, "values should match");
845  TR_STATIC_ASSERT (TR_SEEK_END == FILE_END,     "values should match");
846
847  assert (handle != TR_BAD_SYS_FILE);
848  assert (origin == TR_SEEK_SET || origin == TR_SEEK_CUR || origin == TR_SEEK_END);
849
850  native_offset.QuadPart = offset;
851
852  if (SetFilePointerEx (handle, native_offset, &new_native_pointer, origin))
853    {
854      if (new_offset != NULL)
855        *new_offset = new_native_pointer.QuadPart;
856      ret = true;
857    }
858  else
859    {
860      set_system_error (error, GetLastError ());
861    }
862
863  return ret;
864}
865
866bool
867tr_sys_file_read (tr_sys_file_t    handle,
868                  void           * buffer,
869                  uint64_t         size,
870                  uint64_t       * bytes_read,
871                  tr_error      ** error)
872{
873  bool ret = false;
874  DWORD my_bytes_read;
875
876  assert (handle != TR_BAD_SYS_FILE);
877  assert (buffer != NULL || size == 0);
878
879  if (size > MAXDWORD)
880    {
881      set_system_error (error, ERROR_INVALID_PARAMETER);
882      return false;
883    }
884
885  if (ReadFile (handle, buffer, (DWORD)size, &my_bytes_read, NULL))
886    {
887      if (bytes_read != NULL)
888        *bytes_read = my_bytes_read;
889      ret = true;
890    }
891  else
892    {
893      set_system_error (error, GetLastError ());
894    }
895
896  return ret;
897}
898
899bool
900tr_sys_file_read_at (tr_sys_file_t    handle,
901                     void           * buffer,
902                     uint64_t         size,
903                     uint64_t         offset,
904                     uint64_t       * bytes_read,
905                     tr_error      ** error)
906{
907  bool ret = false;
908  OVERLAPPED overlapped;
909  DWORD my_bytes_read;
910
911  assert (handle != TR_BAD_SYS_FILE);
912  assert (buffer != NULL || size == 0);
913
914  if (size > MAXDWORD)
915    {
916      set_system_error (error, ERROR_INVALID_PARAMETER);
917      return false;
918    }
919
920  overlapped.Offset = (DWORD)offset;
921  offset >>= 32;
922  overlapped.OffsetHigh = (DWORD)offset;
923  overlapped.hEvent = NULL;
924
925  if (ReadFile (handle, buffer, (DWORD)size, &my_bytes_read, &overlapped))
926    {
927      if (bytes_read != NULL)
928        *bytes_read = my_bytes_read;
929      ret = true;
930    }
931  else
932    {
933      set_system_error (error, GetLastError ());
934    }
935
936  return ret;
937}
938
939bool
940tr_sys_file_write (tr_sys_file_t    handle,
941                   const void     * buffer,
942                   uint64_t         size,
943                   uint64_t       * bytes_written,
944                   tr_error      ** error)
945{
946  bool ret = false;
947  DWORD my_bytes_written;
948
949  assert (handle != TR_BAD_SYS_FILE);
950  assert (buffer != NULL || size == 0);
951
952  if (size > MAXDWORD)
953    {
954      set_system_error (error, ERROR_INVALID_PARAMETER);
955      return false;
956    }
957
958  if (WriteFile (handle, buffer, (DWORD)size, &my_bytes_written, NULL))
959    {
960      if (bytes_written != NULL)
961        *bytes_written = my_bytes_written;
962      ret = true;
963    }
964  else
965    {
966      set_system_error (error, GetLastError ());
967    }
968
969  return ret;
970}
971
972bool
973tr_sys_file_write_at (tr_sys_file_t    handle,
974                      const void     * buffer,
975                      uint64_t         size,
976                      uint64_t         offset,
977                      uint64_t       * bytes_written,
978                      tr_error      ** error)
979{
980  bool ret = false;
981  OVERLAPPED overlapped;
982  DWORD my_bytes_written;
983
984  assert (handle != TR_BAD_SYS_FILE);
985  assert (buffer != NULL || size == 0);
986
987  if (size > MAXDWORD)
988    {
989      set_system_error (error, ERROR_INVALID_PARAMETER);
990      return false;
991    }
992
993  overlapped.Offset = (DWORD)offset;
994  offset >>= 32;
995  overlapped.OffsetHigh = (DWORD)offset;
996  overlapped.hEvent = NULL;
997
998  if (WriteFile (handle, buffer, (DWORD)size, &my_bytes_written, &overlapped))
999    {
1000      if (bytes_written != NULL)
1001        *bytes_written = my_bytes_written;
1002      ret = true;
1003    }
1004  else
1005    {
1006      set_system_error (error, GetLastError ());
1007    }
1008
1009  return ret;
1010}
1011
1012bool
1013tr_sys_file_flush (tr_sys_file_t    handle,
1014                   tr_error      ** error)
1015{
1016  bool ret;
1017
1018  assert (handle != TR_BAD_SYS_FILE);
1019
1020  ret = FlushFileBuffers (handle);
1021
1022  if (!ret)
1023    set_system_error (error, GetLastError ());
1024
1025  return ret;
1026}
1027
1028bool
1029tr_sys_file_truncate (tr_sys_file_t    handle,
1030                      uint64_t         size,
1031                      tr_error      ** error)
1032{
1033  bool ret = false;
1034  FILE_END_OF_FILE_INFO info;
1035
1036  assert (handle != TR_BAD_SYS_FILE);
1037
1038  info.EndOfFile.QuadPart = size;
1039
1040  ret = SetFileInformationByHandle (handle, FileEndOfFileInfo, &info, sizeof (info));
1041
1042  if (!ret)
1043    set_system_error (error, GetLastError ());
1044
1045  return ret;
1046}
1047
1048bool
1049tr_sys_file_prefetch (tr_sys_file_t    handle,
1050                      uint64_t         offset,
1051                      uint64_t         size,
1052                      tr_error      ** error)
1053{
1054  bool ret = false;
1055
1056  assert (handle != TR_BAD_SYS_FILE);
1057  assert (size > 0);
1058
1059  (void) handle;
1060  (void) offset;
1061  (void) size;
1062  (void) error;
1063
1064  /* ??? */
1065
1066  return ret;
1067}
1068
1069bool
1070tr_sys_file_preallocate (tr_sys_file_t    handle,
1071                         uint64_t         size,
1072                         int              flags,
1073                         tr_error      ** error)
1074{
1075  assert (handle != TR_BAD_SYS_FILE);
1076
1077  if ((flags & TR_SYS_FILE_PREALLOC_SPARSE) != 0)
1078    {
1079      DWORD tmp;
1080      if (!DeviceIoControl (handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &tmp, NULL))
1081        {
1082          set_system_error (error, GetLastError ());
1083          return false;
1084        }
1085    }
1086
1087  return tr_sys_file_truncate (handle, size, error);
1088}
1089
1090void *
1091tr_sys_file_map_for_reading (tr_sys_file_t    handle,
1092                             uint64_t         offset,
1093                             uint64_t         size,
1094                             tr_error      ** error)
1095{
1096  void * ret = NULL;
1097  HANDLE mappingHandle;
1098
1099  assert (handle != TR_BAD_SYS_FILE);
1100  assert (size > 0);
1101
1102  if (size > MAXSIZE_T)
1103    {
1104      set_system_error (error, ERROR_INVALID_PARAMETER);
1105      return false;
1106    }
1107
1108  mappingHandle = CreateFileMappingW (handle, NULL, PAGE_READONLY, 0, 0, NULL);
1109
1110  if (mappingHandle != NULL)
1111    {
1112      ULARGE_INTEGER native_offset;
1113
1114      native_offset.QuadPart = offset;
1115
1116      ret = MapViewOfFile (mappingHandle, FILE_MAP_READ, native_offset.u.HighPart,
1117                           native_offset.u.LowPart, (SIZE_T)size);
1118    }
1119
1120  if (ret == NULL)
1121    set_system_error (error, GetLastError ());
1122
1123  CloseHandle (mappingHandle);
1124
1125  return ret;
1126}
1127
1128bool
1129tr_sys_file_unmap (const void  * address,
1130                   uint64_t      size,
1131                   tr_error   ** error)
1132{
1133  bool ret;
1134
1135  assert (address != NULL);
1136  assert (size > 0);
1137
1138  (void) size;
1139
1140  ret = UnmapViewOfFile (address);
1141
1142  if (!ret)
1143    set_system_error (error, GetLastError ());
1144
1145  return ret;
1146}
1147
1148char *
1149tr_sys_dir_get_current (tr_error ** error)
1150{
1151  char * ret = NULL;
1152  wchar_t * wide_ret = NULL;
1153  DWORD size;
1154
1155  size = GetCurrentDirectoryW (0, NULL);
1156
1157  if (size != 0)
1158    {
1159      wide_ret = tr_new (wchar_t, size);
1160      if (GetCurrentDirectoryW (size, wide_ret) != 0)
1161        ret = tr_win32_native_to_utf8 (wide_ret, size);
1162    }
1163
1164  if (ret == NULL)
1165    set_system_error (error, GetLastError ());
1166
1167  tr_free (wide_ret);
1168
1169  return ret;
1170}
1171
1172bool
1173tr_sys_dir_create (const char  * path,
1174                   int           flags,
1175                   int           permissions,
1176                   tr_error   ** error)
1177{
1178  return create_dir (path, flags, permissions, true, error);
1179}
1180
1181static void
1182dir_create_temp_callback (const char  * path,
1183                          void        * param,
1184                          tr_error   ** error)
1185{
1186  bool * result = (bool *) param;
1187
1188  assert (result != NULL);
1189
1190  *result = create_dir (path, 0, 0, false, error);
1191}
1192
1193bool
1194tr_sys_dir_create_temp (char      * path_template,
1195                        tr_error ** error)
1196{
1197  bool ret = false;
1198
1199  assert (path_template != NULL);
1200
1201  create_temp_path (path_template, dir_create_temp_callback, &ret, error);
1202
1203  return ret;
1204}
1205
1206tr_sys_dir_t
1207tr_sys_dir_open (const char  * path,
1208                 tr_error   ** error)
1209{
1210  tr_sys_dir_t ret;
1211  int pattern_size;
1212
1213#ifndef __clang__
1214  /* Clang gives "static_assert expression is not an integral constant expression" error */
1215  TR_STATIC_ASSERT (TR_BAD_SYS_DIR == NULL, "values should match");
1216#endif
1217
1218  assert (path != NULL);
1219
1220  ret = tr_new (struct tr_sys_dir_win32, 1);
1221  ret->pattern = path_to_native_path_ex (path, 2, &pattern_size);
1222
1223  if (ret->pattern != NULL)
1224    {
1225      ret->pattern[pattern_size + 0] = L'\\';
1226      ret->pattern[pattern_size + 1] = L'*';
1227
1228      ret->find_handle = INVALID_HANDLE_VALUE;
1229      ret->utf8_name = NULL;
1230    }
1231  else
1232    {
1233      set_system_error (error, GetLastError ());
1234
1235      tr_free (ret->pattern);
1236      tr_free (ret);
1237
1238      ret = NULL;
1239    }
1240
1241  return ret;
1242}
1243
1244const char *
1245tr_sys_dir_read_name (tr_sys_dir_t    handle,
1246                      tr_error     ** error)
1247{
1248  char * ret;
1249  DWORD error_code = ERROR_SUCCESS;
1250
1251  assert (handle != TR_BAD_SYS_DIR);
1252
1253  if (handle->find_handle == INVALID_HANDLE_VALUE)
1254    {
1255      handle->find_handle = FindFirstFileW (handle->pattern, &handle->find_data);
1256      if (handle->find_handle == INVALID_HANDLE_VALUE)
1257        error_code = GetLastError ();
1258    }
1259  else
1260    {
1261      if (!FindNextFileW (handle->find_handle, &handle->find_data))
1262        error_code = GetLastError ();
1263    }
1264
1265  if (error_code != ERROR_SUCCESS)
1266    {
1267      set_system_error_if_file_found (error, error_code);
1268      return NULL;
1269    }
1270
1271  ret = tr_win32_native_to_utf8 (handle->find_data.cFileName, -1);
1272
1273  if (ret != NULL)
1274    {
1275      tr_free (handle->utf8_name);
1276      handle->utf8_name = ret;
1277    }
1278  else
1279    {
1280      set_system_error (error, GetLastError ());
1281    }
1282
1283  return ret;
1284}
1285
1286bool
1287tr_sys_dir_close (tr_sys_dir_t    handle,
1288                  tr_error     ** error)
1289{
1290  bool ret;
1291
1292  assert (handle != TR_BAD_SYS_DIR);
1293
1294  ret = FindClose (handle->find_handle);
1295
1296  if (!ret)
1297    set_system_error (error, GetLastError ());
1298
1299  tr_free (handle->utf8_name);
1300  tr_free (handle->pattern);
1301  tr_free (handle);
1302
1303  return ret;
1304}
Note: See TracBrowser for help on using the repository browser.