source: trunk/libtransmission/file-win32.c @ 14321

Last change on this file since 14321 was 14321, checked in by jordan, 8 years ago

(trunk, libT) #4160 'foreign character support' -- merge mike.dld's 4160-03a-file.platch, which introduces tr_sys_file_*() portability wrappers

File size: 22.5 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$
8 */
9
10#include <assert.h>
11#include <stdlib.h> /* _splitpath_s (), _makepath_s () */
12
13#include <winioctl.h> /* FSCTL_SET_SPARSE */
14
15#include "transmission.h"
16#include "crypto.h" /* tr_cryptoRandInt () */
17#include "file.h"
18#include "utils.h"
19
20#ifndef MAXSIZE_T
21 #define MAXSIZE_T ((SIZE_T)~((SIZE_T)0))
22#endif
23
24/* MSDN (http://msdn.microsoft.com/en-us/library/2k2xf226.aspx) only mentions
25   "i64" suffix for C code, but no warning is issued */
26#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
27
28static void
29set_system_error (tr_error ** error,
30                  DWORD       code)
31{
32  char * message;
33
34  if (error == NULL)
35    return;
36
37  message = tr_win32_format_message (code);
38
39  if (message != NULL)
40    {
41      tr_error_set_literal (error, code, message);
42      tr_free (message);
43    }
44  else
45    {
46      tr_error_set (error, code, "Unknown error: 0x%08x", code);
47    }
48}
49
50static void
51set_system_error_if_file_found (tr_error ** error,
52                                DWORD       code)
53{
54  if (code != ERROR_FILE_NOT_FOUND &&
55      code != ERROR_PATH_NOT_FOUND &&
56      code != ERROR_NO_MORE_FILES)
57    set_system_error (error, code);
58}
59
60static time_t
61filetime_to_unix_time (const FILETIME * t)
62{
63  uint64_t tmp = 0;
64
65  assert (t != NULL);
66
67  tmp |= t->dwHighDateTime;
68  tmp <<= 32;
69  tmp |= t->dwLowDateTime;
70  tmp /= 10; /* to microseconds */
71  tmp -= DELTA_EPOCH_IN_MICROSECS;
72
73  return tmp / 1000000UL;
74}
75
76static void
77stat_to_sys_path_info (DWORD              attributes,
78                       DWORD              size_low,
79                       DWORD              size_high,
80                       const FILETIME   * mtime,
81                       tr_sys_path_info * info)
82{
83  assert (mtime != NULL);
84  assert (info != NULL);
85
86  if (attributes & FILE_ATTRIBUTE_DIRECTORY)
87    info->type = TR_SYS_PATH_IS_DIRECTORY;
88  else if (!(attributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_VIRTUAL)))
89    info->type = TR_SYS_PATH_IS_FILE;
90  else
91    info->type = TR_SYS_PATH_IS_OTHER;
92
93  info->size = size_high;
94  info->size <<= 32;
95  info->size |= size_low;
96
97  info->last_modified_at = filetime_to_unix_time (mtime);
98}
99
100static tr_sys_file_t
101open_file (const char  * path,
102           DWORD         access,
103           DWORD         disposition,
104           DWORD         flags,
105           tr_error   ** error)
106{
107  tr_sys_file_t ret = TR_BAD_SYS_FILE;
108  wchar_t * wide_path;
109
110  assert (path != NULL);
111
112  wide_path = tr_win32_utf8_to_native (path, -1);
113
114  if (wide_path != NULL)
115    ret = CreateFileW (wide_path, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
116                       NULL, disposition, flags, NULL);
117
118  if (ret == TR_BAD_SYS_FILE)
119    set_system_error (error, GetLastError ());
120
121  tr_free (wide_path);
122
123  return ret;
124}
125
126static void
127create_temp_path (char      * path_template,
128                  void     (* callback) (const char * path, void * param, tr_error ** error),
129                  void      * callback_param,
130                  tr_error ** error)
131{
132  char * path;
133  size_t path_size;
134  int attempt;
135  tr_error * my_error = NULL;
136
137  assert (path_template != NULL);
138  assert (callback != NULL);
139
140  path = tr_strdup (path_template);
141  path_size = strlen (path);
142
143  assert (path_size > 0);
144
145  for (attempt = 0; attempt < 100; ++attempt)
146    {
147      size_t i = path_size;
148
149      while (i > 0 && path_template[i - 1] == 'X')
150        {
151          const int c = tr_cryptoRandInt (26 + 26 + 10);
152          path[i - 1] = c < 26 ? c + 'A' : (c < 26 + 26 ? (c - 26) + 'a' : (c - 26 - 26) + '0');
153          --i;
154        }
155
156      assert (path_size >= i + 6);
157
158      tr_error_clear (&my_error);
159
160      (*callback) (path, callback_param, &my_error);
161
162      if (my_error == NULL)
163        break;
164    }
165
166  if (my_error != NULL)
167    tr_error_propagate(error, &my_error);
168  else
169    memcpy (path_template, path, path_size);
170
171  goto cleanup;
172
173fail:
174  set_system_error (error, GetLastError ());
175
176cleanup:
177  tr_free (path);
178}
179
180bool
181tr_sys_path_exists (const char  * path,
182                    tr_error   ** error)
183{
184  bool ret = false;
185  wchar_t * wide_path;
186  HANDLE handle = INVALID_HANDLE_VALUE;
187
188  assert (path != NULL);
189
190  wide_path = tr_win32_utf8_to_native (path, -1);
191
192  if (wide_path != NULL)
193    {
194      DWORD attributes = GetFileAttributesW (wide_path);
195      if (attributes != INVALID_FILE_ATTRIBUTES)
196        {
197          if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
198            {
199              handle = CreateFileW (wide_path, 0, 0, NULL, OPEN_EXISTING,
200                                    FILE_FLAG_BACKUP_SEMANTICS, NULL);
201
202              ret = handle != INVALID_HANDLE_VALUE;
203            }
204          else
205            {
206              ret = true;
207            }
208        }
209    }
210
211  if (!ret)
212    set_system_error_if_file_found (error, GetLastError ());
213
214  if (handle != INVALID_HANDLE_VALUE)
215    CloseHandle (handle);
216
217  tr_free (wide_path);
218
219  return ret;
220}
221
222bool
223tr_sys_path_get_info (const char        * path,
224                      int                 flags,
225                      tr_sys_path_info  * info,
226                      tr_error         ** error)
227{
228  bool ret = false;
229  wchar_t * wide_path;
230
231  assert (path != NULL);
232  assert (info != NULL);
233
234  wide_path = tr_win32_utf8_to_native (path, -1);
235
236  if ((flags & TR_SYS_PATH_NO_FOLLOW) == 0)
237    {
238      HANDLE handle = INVALID_HANDLE_VALUE;
239
240      if (wide_path != NULL)
241        handle = CreateFileW (wide_path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
242
243      if (handle != INVALID_HANDLE_VALUE)
244        {
245          tr_error * my_error = NULL;
246          ret = tr_sys_file_get_info (handle, info, &my_error);
247          if (!ret)
248            tr_error_propagate (error, &my_error);
249          CloseHandle (handle);
250        }
251      else
252        {
253          set_system_error (error, GetLastError ());
254        }
255    }
256  else
257    {
258      WIN32_FILE_ATTRIBUTE_DATA attributes;
259
260      if (wide_path != NULL)
261        ret = GetFileAttributesExW (wide_path, GetFileExInfoStandard, &attributes);
262
263      if (ret)
264        stat_to_sys_path_info (attributes.dwFileAttributes, attributes.nFileSizeLow,
265                               attributes.nFileSizeHigh, &attributes.ftLastWriteTime,
266                               info);
267      else
268        set_system_error (error, GetLastError ());
269    }
270
271  tr_free (wide_path);
272
273  return ret;
274}
275
276bool
277tr_sys_path_is_same (const char  * path1,
278                     const char  * path2,
279                     tr_error   ** error)
280{
281  bool ret = false;
282  wchar_t * wide_path1 = NULL;
283  wchar_t * wide_path2 = NULL;
284  HANDLE handle1 = INVALID_HANDLE_VALUE;
285  HANDLE handle2 = INVALID_HANDLE_VALUE;
286  BY_HANDLE_FILE_INFORMATION fi1, fi2;
287
288  assert (path1 != NULL);
289  assert (path2 != NULL);
290
291  wide_path1 = tr_win32_utf8_to_native (path1, -1);
292  if (wide_path1 == NULL)
293    goto fail;
294
295  wide_path2 = tr_win32_utf8_to_native (path2, -1);
296  if (wide_path2 == NULL)
297    goto fail;
298
299  handle1 = CreateFileW (wide_path1, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
300  if (handle1 == INVALID_HANDLE_VALUE)
301    goto fail;
302
303  handle2 = CreateFileW (wide_path2, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
304  if (handle2 == INVALID_HANDLE_VALUE)
305    goto fail;
306
307  /* TODO: Use GetFileInformationByHandleEx on >= Server 2012 */
308
309  if (!GetFileInformationByHandle (handle1, &fi1) || !GetFileInformationByHandle (handle2, &fi2))
310    goto fail;
311
312  ret = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
313        fi1.nFileIndexHigh == fi2.nFileIndexHigh &&
314        fi1.nFileIndexLow  == fi2.nFileIndexLow;
315
316  goto cleanup;
317
318fail:
319  set_system_error_if_file_found (error, GetLastError ());
320
321cleanup:
322  CloseHandle (handle2);
323  CloseHandle (handle1);
324
325  tr_free (wide_path2);
326  tr_free (wide_path1);
327
328  return ret;
329}
330
331char *
332tr_sys_path_resolve (const char  * path,
333                     tr_error   ** error)
334{
335  char * ret = NULL;
336  wchar_t * wide_path;
337  wchar_t * wide_ret = NULL;
338  HANDLE handle;
339  DWORD wide_ret_size;
340
341  assert (path != NULL);
342
343  wide_path = tr_win32_utf8_to_native (path, -1);
344  if (wide_path == NULL)
345    goto fail;
346
347  handle = CreateFileW (wide_path, FILE_READ_EA,
348                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
349                        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
350  if (handle == INVALID_HANDLE_VALUE)
351    goto fail;
352
353  wide_ret_size = GetFinalPathNameByHandleW (handle, NULL, 0, 0);
354  if (wide_ret_size == 0)
355    goto fail;
356
357  wide_ret = tr_new (wchar_t, wide_ret_size);
358  if (GetFinalPathNameByHandleW (handle, wide_ret, wide_ret_size, 0) != wide_ret_size - 1)
359    goto fail;
360
361  /* Resolved path always begins with "\\?\", so skip those first four chars. */
362  ret = tr_win32_native_to_utf8 (wide_ret + 4, -1);
363  if (ret != NULL)
364    goto cleanup;
365
366fail:
367  set_system_error (error, GetLastError ());
368
369  tr_free (ret);
370  ret = NULL;
371
372cleanup:
373  tr_free (wide_ret);
374  tr_free (wide_path);
375
376  if (handle != INVALID_HANDLE_VALUE)
377    CloseHandle (handle);
378
379  return ret;
380}
381
382char *
383tr_sys_path_basename (const char  * path,
384                      tr_error   ** error)
385{
386  char fname[_MAX_FNAME], ext[_MAX_EXT];
387
388  assert (path != NULL);
389
390  /* TODO: Error handling */
391
392  if (_splitpath_s (path, NULL, 0, NULL, 0, fname, sizeof (fname), ext, sizeof (ext)) == 0 &&
393      (*fname != '\0' || *ext != '\0'))
394    {
395      const size_t tmp_len = strlen (fname) + strlen (ext) + 2;
396      char * const tmp = tr_new (char, tmp_len);
397      if (_makepath_s (tmp, tmp_len, NULL, NULL, fname, ext) == 0)
398        return tmp;
399      tr_free (tmp);
400    }
401
402  return tr_strdup (".");
403}
404
405char *
406tr_sys_path_dirname (const char  * path,
407                     tr_error   ** error)
408{
409  char drive[_MAX_DRIVE], dir[_MAX_DIR];
410
411  assert (path != NULL);
412
413  /* TODO: Error handling */
414
415  if (_splitpath_s (path, drive, sizeof (drive), dir, sizeof (dir), NULL, 0, NULL, 0) == 0 &&
416      (*drive != '\0' || *dir != '\0'))
417    {
418      const size_t tmp_len = strlen (drive) + strlen (dir) + 2;
419      char * const tmp = tr_new (char, tmp_len);
420      if (_makepath_s (tmp, tmp_len, drive, dir, NULL, NULL) == 0)
421        {
422          size_t len = strlen(tmp);
423          while (len > 0 && (tmp[len - 1] == '/' || tmp[len - 1] == '\\'))
424            tmp[--len] = '\0';
425
426          return tmp;
427        }
428
429      tr_free (tmp);
430    }
431
432  return tr_strdup (".");
433}
434
435bool
436tr_sys_path_rename (const char  * src_path,
437                    const char  * dst_path,
438                    tr_error   ** error)
439{
440  bool ret = false;
441  wchar_t * wide_src_path;
442  wchar_t * wide_dst_path;
443
444  assert (src_path != NULL);
445  assert (dst_path != NULL);
446
447  wide_src_path = tr_win32_utf8_to_native (src_path, -1);
448  wide_dst_path = tr_win32_utf8_to_native (dst_path, -1);
449
450  if (wide_src_path != NULL && wide_dst_path != NULL)
451    {
452      DWORD flags = MOVEFILE_REPLACE_EXISTING;
453      DWORD attributes;
454
455      attributes = GetFileAttributesW (wide_src_path);
456      if (attributes != INVALID_FILE_ATTRIBUTES &&
457          (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
458        {
459          flags = 0;
460        }
461      else
462        {
463          attributes = GetFileAttributesW (wide_dst_path);
464          if (attributes != INVALID_FILE_ATTRIBUTES &&
465              (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
466            flags = 0;
467        }
468
469      ret = MoveFileExW (wide_src_path, wide_dst_path, flags);
470    }
471
472  if (!ret)
473    set_system_error (error, GetLastError ());
474
475  tr_free (wide_dst_path);
476  tr_free (wide_src_path);
477
478  return ret;
479}
480
481bool
482tr_sys_path_remove (const char  * path,
483                    tr_error   ** error)
484{
485  bool ret = false;
486  wchar_t * wide_path;
487
488  assert (path != NULL);
489
490  wide_path = tr_win32_utf8_to_native (path, -1);
491
492  if (wide_path != NULL)
493    {
494      const DWORD attributes = GetFileAttributesW (wide_path);
495
496      if (attributes != INVALID_FILE_ATTRIBUTES)
497        {
498          if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
499            ret = RemoveDirectoryW (wide_path);
500          else
501            ret = DeleteFileW (wide_path);
502        }
503    }
504
505  if (!ret)
506    set_system_error (error, GetLastError ());
507
508  tr_free (wide_path);
509
510  return ret;
511}
512
513tr_sys_file_t
514tr_sys_file_get_std (tr_std_sys_file_t    std_file,
515                     tr_error          ** error)
516{
517  tr_sys_file_t ret = TR_BAD_SYS_FILE;
518
519  switch (std_file)
520    {
521    case TR_STD_SYS_FILE_IN:
522      ret = GetStdHandle (STD_INPUT_HANDLE);
523      break;
524    case TR_STD_SYS_FILE_OUT:
525      ret = GetStdHandle (STD_OUTPUT_HANDLE);
526      break;
527    case TR_STD_SYS_FILE_ERR:
528      ret = GetStdHandle (STD_ERROR_HANDLE);
529      break;
530    default:
531      assert (0 && "Unknown standard file");
532      set_system_error (error, ERROR_INVALID_PARAMETER);
533      return TR_BAD_SYS_FILE;
534    }
535
536  if (ret == TR_BAD_SYS_FILE)
537    set_system_error (error, GetLastError ());
538  else if (ret == NULL)
539    ret = TR_BAD_SYS_FILE;
540
541  return ret;
542}
543
544tr_sys_file_t
545tr_sys_file_open (const char  * path,
546                  int           flags,
547                  int           permissions,
548                  tr_error   ** error)
549{
550  tr_sys_file_t ret;
551  DWORD native_access = 0;
552  DWORD native_disposition = OPEN_EXISTING;
553  DWORD native_flags = FILE_ATTRIBUTE_NORMAL;
554  bool success;
555
556  assert (path != NULL);
557  assert ((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) != 0);
558
559  if (flags & TR_SYS_FILE_READ)
560    native_access |= GENERIC_READ;
561  if (flags & TR_SYS_FILE_WRITE)
562    native_access |= GENERIC_WRITE;
563
564  if (flags & TR_SYS_FILE_CREATE_NEW)
565    native_disposition = CREATE_NEW;
566  else if (flags & TR_SYS_FILE_CREATE)
567    native_disposition = flags & TR_SYS_FILE_TRUNCATE ? CREATE_ALWAYS : OPEN_ALWAYS;
568  else if (flags & TR_SYS_FILE_TRUNCATE)
569    native_disposition = TRUNCATE_EXISTING;
570
571  if (flags & TR_SYS_FILE_SEQUENTIAL)
572    native_flags |= FILE_FLAG_SEQUENTIAL_SCAN;
573
574  ret = open_file (path, native_access, native_disposition, native_flags, error);
575
576  success = ret != TR_BAD_SYS_FILE;
577
578  if (success && (flags & TR_SYS_FILE_APPEND))
579    success = SetFilePointer (ret, 0, NULL, FILE_END) != INVALID_SET_FILE_POINTER;
580
581  if (!success)
582    {
583      if (error == NULL)
584        set_system_error (error, GetLastError ());
585
586      CloseHandle (ret);
587      ret = TR_BAD_SYS_FILE;
588    }
589
590  return ret;
591}
592
593static void
594file_open_temp_callback (const char  * path,
595                         void        * param,
596                         tr_error   ** error)
597{
598  tr_sys_file_t * result = (tr_sys_file_t *) param;
599
600  assert (result != NULL);
601
602  *result = open_file (path,
603                       GENERIC_READ | GENERIC_WRITE,
604                       CREATE_NEW,
605                       FILE_ATTRIBUTE_TEMPORARY,
606                       error);
607}
608
609tr_sys_file_t
610tr_sys_file_open_temp (char      * path_template,
611                       tr_error ** error)
612{
613  tr_sys_file_t ret = TR_BAD_SYS_FILE;
614
615  assert (path_template != NULL);
616
617  create_temp_path (path_template, file_open_temp_callback, &ret, error);
618
619  return ret;
620}
621
622bool
623tr_sys_file_close (tr_sys_file_t    handle,
624                   tr_error      ** error)
625{
626  bool ret;
627
628  assert (handle != TR_BAD_SYS_FILE);
629
630  ret = CloseHandle (handle);
631
632  if (!ret)
633    set_system_error (error, GetLastError ());
634
635  return ret;
636}
637
638bool
639tr_sys_file_get_info (tr_sys_file_t       handle,
640                      tr_sys_path_info  * info,
641                      tr_error         ** error)
642{
643  bool ret;
644  BY_HANDLE_FILE_INFORMATION attributes;
645
646  assert (handle != TR_BAD_SYS_FILE);
647  assert (info != NULL);
648
649  ret = GetFileInformationByHandle (handle, &attributes);
650
651  if (ret)
652    stat_to_sys_path_info (attributes.dwFileAttributes, attributes.nFileSizeLow,
653                           attributes.nFileSizeHigh, &attributes.ftLastWriteTime,
654                           info);
655  else
656    set_system_error (error, GetLastError ());
657
658  return ret;
659}
660
661bool
662tr_sys_file_seek (tr_sys_file_t       handle,
663                  int64_t             offset,
664                  tr_seek_origin_t    origin,
665                  uint64_t          * new_offset,
666                  tr_error         ** error)
667{
668  bool ret = false;
669  LARGE_INTEGER native_offset, new_native_pointer;
670
671  TR_STATIC_ASSERT (TR_SEEK_SET == FILE_BEGIN,   "values should match");
672  TR_STATIC_ASSERT (TR_SEEK_CUR == FILE_CURRENT, "values should match");
673  TR_STATIC_ASSERT (TR_SEEK_END == FILE_END,     "values should match");
674
675  assert (handle != TR_BAD_SYS_FILE);
676  assert (origin == TR_SEEK_SET || origin == TR_SEEK_CUR || origin == TR_SEEK_END);
677
678  native_offset.QuadPart = offset;
679
680  if (SetFilePointerEx (handle, native_offset, &new_native_pointer, origin))
681    {
682      if (new_offset != NULL)
683        *new_offset = new_native_pointer.QuadPart;
684      ret = true;
685    }
686  else
687    {
688      set_system_error (error, GetLastError ());
689    }
690
691  return ret;
692}
693
694bool
695tr_sys_file_read (tr_sys_file_t    handle,
696                  void           * buffer,
697                  uint64_t         size,
698                  uint64_t       * bytes_read,
699                  tr_error      ** error)
700{
701  bool ret = false;
702  DWORD my_bytes_read;
703
704  assert (handle != TR_BAD_SYS_FILE);
705  assert (buffer != NULL || size == 0);
706
707  if (size > MAXDWORD)
708    {
709      set_system_error (error, ERROR_INVALID_PARAMETER);
710      return false;
711    }
712
713  if (ReadFile (handle, buffer, (DWORD)size, &my_bytes_read, NULL))
714    {
715      if (bytes_read != NULL)
716        *bytes_read = my_bytes_read;
717      ret = true;
718    }
719  else
720    {
721      set_system_error (error, GetLastError ());
722    }
723
724  return ret;
725}
726
727bool
728tr_sys_file_read_at (tr_sys_file_t    handle,
729                     void           * buffer,
730                     uint64_t         size,
731                     uint64_t         offset,
732                     uint64_t       * bytes_read,
733                     tr_error      ** error)
734{
735  bool ret = false;
736  OVERLAPPED overlapped;
737  DWORD my_bytes_read;
738
739  assert (handle != TR_BAD_SYS_FILE);
740  assert (buffer != NULL || size == 0);
741
742  if (size > MAXDWORD)
743    {
744      set_system_error (error, ERROR_INVALID_PARAMETER);
745      return false;
746    }
747
748  overlapped.Offset = (DWORD)offset;
749  offset >>= 32;
750  overlapped.OffsetHigh = (DWORD)offset;
751  overlapped.hEvent = NULL;
752
753  if (ReadFile (handle, buffer, (DWORD)size, &my_bytes_read, &overlapped))
754    {
755      if (bytes_read != NULL)
756        *bytes_read = my_bytes_read;
757      ret = true;
758    }
759  else
760    {
761      set_system_error (error, GetLastError ());
762    }
763
764  return ret;
765}
766
767bool
768tr_sys_file_write (tr_sys_file_t    handle,
769                   const void     * buffer,
770                   uint64_t         size,
771                   uint64_t       * bytes_written,
772                   tr_error      ** error)
773{
774  bool ret = false;
775  DWORD my_bytes_written;
776
777  assert (handle != TR_BAD_SYS_FILE);
778  assert (buffer != NULL || size == 0);
779
780  if (size > MAXDWORD)
781    {
782      set_system_error (error, ERROR_INVALID_PARAMETER);
783      return false;
784    }
785
786  if (WriteFile (handle, buffer, (DWORD)size, &my_bytes_written, NULL))
787    {
788      if (bytes_written != NULL)
789        *bytes_written = my_bytes_written;
790      ret = true;
791    }
792  else
793    {
794      set_system_error (error, GetLastError ());
795    }
796
797  return ret;
798}
799
800bool
801tr_sys_file_write_at (tr_sys_file_t    handle,
802                      const void     * buffer,
803                      uint64_t         size,
804                      uint64_t         offset,
805                      uint64_t       * bytes_written,
806                      tr_error      ** error)
807{
808  bool ret = false;
809  OVERLAPPED overlapped;
810  DWORD my_bytes_written;
811
812  assert (handle != TR_BAD_SYS_FILE);
813  assert (buffer != NULL || size == 0);
814
815  if (size > MAXDWORD)
816    {
817      set_system_error (error, ERROR_INVALID_PARAMETER);
818      return false;
819    }
820
821  overlapped.Offset = (DWORD)offset;
822  offset >>= 32;
823  overlapped.OffsetHigh = (DWORD)offset;
824  overlapped.hEvent = NULL;
825
826  if (WriteFile (handle, buffer, (DWORD)size, &my_bytes_written, &overlapped))
827    {
828      if (bytes_written != NULL)
829        *bytes_written = my_bytes_written;
830      ret = true;
831    }
832  else
833    {
834      set_system_error (error, GetLastError ());
835    }
836
837  return ret;
838}
839
840bool
841tr_sys_file_flush (tr_sys_file_t    handle,
842                   tr_error      ** error)
843{
844  bool ret;
845
846  assert (handle != TR_BAD_SYS_FILE);
847
848  ret = FlushFileBuffers (handle);
849
850  if (!ret)
851    set_system_error (error, GetLastError ());
852
853  return ret;
854}
855
856bool
857tr_sys_file_truncate (tr_sys_file_t    handle,
858                      uint64_t         size,
859                      tr_error      ** error)
860{
861  bool ret = false;
862  FILE_END_OF_FILE_INFO info;
863
864  assert (handle != TR_BAD_SYS_FILE);
865
866  info.EndOfFile.QuadPart = size;
867
868  ret = SetFileInformationByHandle (handle, FileEndOfFileInfo, &info, sizeof (info));
869
870  if (!ret)
871    set_system_error (error, GetLastError ());
872
873  return ret;
874}
875
876bool
877tr_sys_file_prefetch (tr_sys_file_t    handle,
878                      uint64_t         offset,
879                      uint64_t         size,
880                      tr_error      ** error)
881{
882  bool ret = false;
883
884  assert (handle != TR_BAD_SYS_FILE);
885  assert (size > 0);
886
887  /* ??? */
888
889  return ret;
890}
891
892bool
893tr_sys_file_preallocate (tr_sys_file_t    handle,
894                         uint64_t         size,
895                         int              flags,
896                         tr_error      ** error)
897{
898  assert (handle != TR_BAD_SYS_FILE);
899
900  if ((flags & TR_SYS_FILE_PREALLOC_SPARSE) != 0)
901    {
902      DWORD tmp;
903      if (!DeviceIoControl (handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &tmp, NULL))
904        {
905          set_system_error (error, GetLastError ());
906          return false;
907        }
908    }
909
910  return tr_sys_file_truncate (handle, size, error);
911}
912
913void *
914tr_sys_file_map_for_reading (tr_sys_file_t    handle,
915                             uint64_t         offset,
916                             uint64_t         size,
917                             tr_error      ** error)
918{
919  void * ret = NULL;
920  HANDLE mappingHandle;
921
922  assert (handle != TR_BAD_SYS_FILE);
923  assert (size > 0);
924
925  if (size > MAXSIZE_T)
926    {
927      set_system_error (error, ERROR_INVALID_PARAMETER);
928      return false;
929    }
930
931  mappingHandle = CreateFileMappingW (handle, NULL, PAGE_READONLY, 0, 0, NULL);
932
933  if (mappingHandle != NULL)
934    {
935      ULARGE_INTEGER native_offset;
936
937      native_offset.QuadPart = offset;
938
939      ret = MapViewOfFile (mappingHandle, FILE_MAP_READ, native_offset.u.HighPart,
940                           native_offset.u.LowPart, (SIZE_T)size);
941    }
942
943  if (ret == NULL)
944    set_system_error (error, GetLastError ());
945
946  CloseHandle (mappingHandle);
947
948  return ret;
949}
950
951bool
952tr_sys_file_unmap (const void  * address,
953                   uint64_t      size,
954                   tr_error   ** error)
955{
956  bool ret;
957
958  assert (address != NULL);
959  assert (size > 0);
960
961  ret = UnmapViewOfFile (address);
962
963  if (!ret)
964    set_system_error (error, GetLastError ());
965
966  return ret;
967}
Note: See TracBrowser for help on using the repository browser.