cdbe0a4f018a778ae00698ac4590d29ec7d6f851
[metze/wireshark/wip.git] / wsutil / file_util.c
1 /* file_util.c
2  *
3  * (Originally part of the Wiretap Library, now part of the Wireshark
4  *  utility library)
5  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  */
22
23 /*
24  * File wrapper functions to replace the file functions from GLib like
25  * g_open().
26  *
27  * With MSVC, code using the C support library from one version of MSVC
28  * cannot use file descriptors or FILE *'s returned from code using
29  * the C support library from another version of MSVC.
30  *
31  * We therefore provide our own versions of the routines to open files,
32  * so that they're built to use the same C support library as our code
33  * that reads them.
34  *
35  * (If both were built to use the Universal CRT:
36  *
37  *    http://blogs.msdn.com/b/vcblog/archive/2015/03/03/introducing-the-universal-crt.aspx
38  *
39  * this would not be a problem.)
40  *
41  * DO NOT USE THESE FUNCTIONS DIRECTLY, USE ws_open() AND ALIKE FUNCTIONS
42  * FROM file_util.h INSTEAD!!!
43  *
44  * The following code is stripped down code copied from the GLib file
45  * glib/gstdio.h - stripped down because this is used only on Windows
46  * and we use only wide char functions.
47  *
48  * In addition, we have our own ws_stdio_stat64(), which uses
49  * _wstati64(), so that we can get file sizes for files > 4 GB in size.
50  *
51  * XXX - is there any reason why we supply our own versions of routines
52  * that *don't* return file descriptors, other than ws_stdio_stat64()?
53  * Is there an issue with UTF-16 support in _wmkdir() with some versions
54  * of the C runtime, so that if GLib is built to use that version, it
55  * won't handle UTF-16 paths?
56  */
57
58 #ifndef _WIN32
59 #error "This is only for Windows"
60 #endif
61
62 #include "config.h"
63
64 #include <glib.h>
65
66 #include <windows.h>
67 #include <errno.h>
68 #include <wchar.h>
69 #include <tchar.h>
70 #include <stdlib.h>
71
72 #include "file_util.h"
73
74 static gchar *program_path = NULL;
75 static gchar *system_path = NULL;
76 static gchar *npcap_path = NULL;
77
78 /**
79  * g_open:
80  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
81  * @flags: as in open()
82  * @mode: as in open()
83  *
84  * A wrapper for the POSIX open() function. The open() function is
85  * used to convert a pathname into a file descriptor. Note that on
86  * POSIX systems file descriptors are implemented by the operating
87  * system. On Windows, it's the C library that implements open() and
88  * file descriptors. The actual Windows API for opening files is
89  * something different.
90  *
91  * See the C library manual for more details about open().
92  *
93  * Returns: a new file descriptor, or -1 if an error occurred. The
94  * return value can be used exactly like the return value from open().
95  *
96  * Since: 2.6
97  */
98 int
99 ws_stdio_open (const gchar *filename,
100         int          flags,
101         int          mode)
102 {
103       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
104       int retval;
105       int save_errno;
106
107       if (wfilename == NULL)
108         {
109           errno = EINVAL;
110           return -1;
111         }
112
113       retval = _wopen (wfilename, flags, mode);
114       save_errno = errno;
115
116       g_free (wfilename);
117
118       errno = save_errno;
119       return retval;
120 }
121
122
123 /**
124  * g_rename:
125  * @oldfilename: a pathname in the GLib file name encoding (UTF-8 on Windows)
126  * @newfilename: a pathname in the GLib file name encoding
127  *
128  * A wrapper for the POSIX rename() function. The rename() function
129  * renames a file, moving it between directories if required.
130  *
131  * See your C library manual for more details about how rename() works
132  * on your system. Note in particular that on Win9x it is not possible
133  * to rename a file if a file with the new name already exists. Also
134  * it is not possible in general on Windows to rename an open file.
135  *
136  * Returns: 0 if the renaming succeeded, -1 if an error occurred
137  *
138  * Since: 2.6
139  */
140 int
141 ws_stdio_rename (const gchar *oldfilename,
142           const gchar *newfilename)
143 {
144       wchar_t *woldfilename = g_utf8_to_utf16 (oldfilename, -1, NULL, NULL, NULL);
145       wchar_t *wnewfilename;
146       int retval;
147       int save_errno = 0;
148
149       if (woldfilename == NULL)
150         {
151           errno = EINVAL;
152           return -1;
153         }
154
155       wnewfilename = g_utf8_to_utf16 (newfilename, -1, NULL, NULL, NULL);
156
157       if (wnewfilename == NULL)
158         {
159           g_free (woldfilename);
160           errno = EINVAL;
161           return -1;
162         }
163
164       if (MoveFileExW (woldfilename, wnewfilename, MOVEFILE_REPLACE_EXISTING))
165         retval = 0;
166       else
167         {
168           retval = -1;
169           switch (GetLastError ())
170             {
171 #define CASE(a,b) case ERROR_##a: save_errno = b; break
172             CASE (FILE_NOT_FOUND, ENOENT);
173             CASE (PATH_NOT_FOUND, ENOENT);
174             CASE (ACCESS_DENIED, EACCES);
175             CASE (NOT_SAME_DEVICE, EXDEV);
176             CASE (LOCK_VIOLATION, EACCES);
177             CASE (SHARING_VIOLATION, EACCES);
178             CASE (FILE_EXISTS, EEXIST);
179             CASE (ALREADY_EXISTS, EEXIST);
180 #undef CASE
181             default: save_errno = EIO;
182             }
183         }
184
185       g_free (woldfilename);
186       g_free (wnewfilename);
187
188       errno = save_errno;
189       return retval;
190 }
191
192 /**
193  * g_mkdir:
194  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
195  * @mode: permissions to use for the newly created directory
196  *
197  * A wrapper for the POSIX mkdir() function. The mkdir() function
198  * attempts to create a directory with the given name and permissions.
199  *
200  * See the C library manual for more details about mkdir().
201  *
202  * Returns: 0 if the directory was successfully created, -1 if an error
203  *    occurred
204  *
205  * Since: 2.6
206  */
207 int
208 ws_stdio_mkdir (const gchar *filename,
209          int          mode)
210 {
211       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
212       int retval;
213       int save_errno;
214
215       if (wfilename == NULL)
216         {
217           errno = EINVAL;
218           return -1;
219         }
220
221       retval = _wmkdir (wfilename);
222       save_errno = errno;
223
224       g_free (wfilename);
225
226       errno = save_errno;
227       return retval;
228 }
229
230 /**
231  * g_stat:
232  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
233  * @buf: a pointer to a <structname>stat</structname> struct, which
234  *    will be filled with the file information
235  *
236  * A wrapper for the POSIX stat() function. The stat() function
237  * returns information about a file.
238  *
239  * See the C library manual for more details about stat().
240  *
241  * Returns: 0 if the information was successfully retrieved, -1 if an error
242  *    occurred
243  *
244  * Since: 2.6
245  */
246 int
247 ws_stdio_stat64 (const gchar *filename,
248         ws_statb64 *buf)
249 {
250       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
251       int retval;
252       int save_errno;
253       size_t len;
254
255       if (wfilename == NULL)
256         {
257           errno = EINVAL;
258           return -1;
259         }
260
261       len = wcslen (wfilename);
262       while (len > 0 && G_IS_DIR_SEPARATOR (wfilename[len-1]))
263         len--;
264       if (len > 0 &&
265           (!g_path_is_absolute (filename) || len > (size_t) (g_path_skip_root (filename) - filename)))
266         wfilename[len] = '\0';
267
268       retval = _wstati64 (wfilename, buf);
269       save_errno = errno;
270
271       g_free (wfilename);
272
273       errno = save_errno;
274       return retval;
275 }
276 /**
277  * g_unlink:
278  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
279  *
280  * A wrapper for the POSIX unlink() function. The unlink() function
281  * deletes a name from the filesystem. If this was the last link to the
282  * file and no processes have it opened, the diskspace occupied by the
283  * file is freed.
284  *
285  * See your C library manual for more details about unlink(). Note
286  * that on Windows, it is in general not possible to delete files that
287  * are open to some process, or mapped into memory.
288  *
289  * Returns: 0 if the name was successfully deleted, -1 if an error
290  *    occurred
291  *
292  * Since: 2.6
293  */
294
295 int
296 ws_stdio_unlink (const gchar *filename)
297 {
298       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
299       int retval;
300       int save_errno;
301
302       if (wfilename == NULL)
303         {
304           errno = EINVAL;
305           return -1;
306         }
307
308       retval = _wunlink (wfilename);
309       save_errno = errno;
310
311       g_free (wfilename);
312
313       errno = save_errno;
314       return retval;
315 }
316
317 /**
318  * g_remove:
319  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
320  *
321  * A wrapper for the POSIX remove() function. The remove() function
322  * deletes a name from the filesystem.
323  *
324  * See your C library manual for more details about how remove() works
325  * on your system. On Unix, remove() removes also directories, as it
326  * calls unlink() for files and rmdir() for directories. On Windows,
327  * although remove() in the C library only works for files, this
328  * function tries first remove() and then if that fails rmdir(), and
329  * thus works for both files and directories. Note however, that on
330  * Windows, it is in general not possible to remove a file that is
331  * open to some process, or mapped into memory.
332  *
333  * If this function fails on Windows you can't infer too much from the
334  * errno value. rmdir() is tried regardless of what caused remove() to
335  * fail. Any errno value set by remove() will be overwritten by that
336  * set by rmdir().
337  *
338  * Returns: 0 if the file was successfully removed, -1 if an error
339  *    occurred
340  *
341  * Since: 2.6
342  */
343 int
344 ws_stdio_remove (const gchar *filename)
345 {
346       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
347       int retval;
348       int save_errno;
349
350       if (wfilename == NULL)
351         {
352           errno = EINVAL;
353           return -1;
354         }
355
356       retval = _wremove (wfilename);
357       if (retval == -1)
358         retval = _wrmdir (wfilename);
359       save_errno = errno;
360
361       g_free (wfilename);
362
363       errno = save_errno;
364       return retval;
365 }
366
367 /**
368  * g_fopen:
369  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
370  * @mode: a string describing the mode in which the file should be
371  *   opened
372  *
373  * A wrapper for the POSIX fopen() function. The fopen() function opens
374  * a file and associates a new stream with it.
375  *
376  * See the C library manual for more details about fopen().
377  *
378  * Returns: A <type>FILE</type> pointer if the file was successfully
379  *    opened, or %NULL if an error occurred
380  *
381  * Since: 2.6
382  */
383 FILE *
384 ws_stdio_fopen (const gchar *filename,
385          const gchar *mode)
386 {
387       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
388       wchar_t *wmode;
389       FILE *retval;
390       int save_errno;
391
392       if (wfilename == NULL)
393         {
394           errno = EINVAL;
395           return NULL;
396         }
397
398       wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
399
400       if (wmode == NULL)
401         {
402           g_free (wfilename);
403           errno = EINVAL;
404           return NULL;
405         }
406
407       retval = _wfopen (wfilename, wmode);
408       save_errno = errno;
409
410       g_free (wfilename);
411       g_free (wmode);
412
413       errno = save_errno;
414       return retval;
415 }
416
417 /**
418  * g_freopen:
419  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
420  * @mode: a string describing the mode in which the file should be
421  *   opened
422  * @stream: an existing stream which will be reused, or %NULL
423  *
424  * A wrapper for the POSIX freopen() function. The freopen() function
425  * opens a file and associates it with an existing stream.
426  *
427  * See the C library manual for more details about freopen().
428  *
429  * Returns: A <type>FILE</type> pointer if the file was successfully
430  *    opened, or %NULL if an error occurred.
431  *
432  * Since: 2.6
433  */
434 FILE *
435 ws_stdio_freopen (const gchar *filename,
436            const gchar *mode,
437            FILE        *stream)
438 {
439       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
440       wchar_t *wmode;
441       FILE *retval;
442       int save_errno;
443
444       if (wfilename == NULL)
445         {
446           errno = EINVAL;
447           return NULL;
448         }
449
450       wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
451
452       if (wmode == NULL)
453         {
454           g_free (wfilename);
455           errno = EINVAL;
456           return NULL;
457         }
458
459       retval = _wfreopen (wfilename, wmode, stream);
460       save_errno = errno;
461
462       g_free (wfilename);
463       g_free (wmode);
464
465       errno = save_errno;
466       return retval;
467 }
468
469
470 /* DLL loading */
471 static gboolean
472 init_dll_load_paths()
473 {
474       TCHAR path_w[MAX_PATH];
475
476       if (program_path && system_path && npcap_path)
477             return TRUE;
478
479       /* XXX - Duplicate code in filesystem.c:init_progfile_dir */
480       if (GetModuleFileName(NULL, path_w, MAX_PATH) == 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
481             return FALSE;
482       }
483
484       if (!program_path) {
485             gchar *app_path;
486             app_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
487             /* We could use PathRemoveFileSpec here but we'd have to link to Shlwapi.dll */
488             program_path = g_path_get_dirname(app_path);
489             g_free(app_path);
490       }
491
492       if (GetSystemDirectory(path_w, MAX_PATH) == 0) {
493             return FALSE;
494       }
495
496       if (!system_path) {
497             system_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
498       }
499
500       _tcscat_s(path_w, MAX_PATH, _T("\\Npcap"));
501
502       if (!npcap_path) {
503             npcap_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
504       }
505
506       if (program_path && system_path && npcap_path)
507             return TRUE;
508
509       return FALSE;
510 }
511
512 gboolean
513 ws_init_dll_search_path()
514 {
515       gboolean dll_dir_set = FALSE;
516       wchar_t *program_path_w;
517       wchar_t npcap_path_w[MAX_PATH];
518       unsigned int retval;
519
520       typedef BOOL (WINAPI *SetDllDirectoryHandler)(LPCTSTR);
521       SetDllDirectoryHandler PSetDllDirectory;
522
523       PSetDllDirectory = (SetDllDirectoryHandler) GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "SetDllDirectoryW");
524       if (PSetDllDirectory) {
525             dll_dir_set = PSetDllDirectory(_T(""));
526             if (dll_dir_set) {
527                   retval = GetSystemDirectoryW(npcap_path_w, MAX_PATH);
528                   if (0 < retval && retval <= MAX_PATH) {
529                         wcscat_s(npcap_path_w, MAX_PATH, L"\\Npcap");
530                         dll_dir_set = PSetDllDirectory(npcap_path_w);
531                   }
532             }
533       }
534
535       if (!dll_dir_set && init_dll_load_paths()) {
536             program_path_w = g_utf8_to_utf16(program_path, -1, NULL, NULL, NULL);
537             SetCurrentDirectory(program_path_w);
538             g_free(program_path_w);
539       }
540
541       return dll_dir_set;
542 }
543
544 /*
545  * Internally g_module_open uses LoadLibrary on Windows and returns an
546  * HMODULE cast to a GModule *. However there's no guarantee that this
547  * will always be the case, so we call LoadLibrary and g_module_open
548  * separately.
549  */
550
551 void *
552 ws_load_library(const gchar *library_name)
553 {
554       gchar   *full_path;
555       wchar_t *full_path_w;
556       HMODULE  dll_h;
557
558       if (!init_dll_load_paths() || !library_name)
559             return NULL;
560
561       /* First try the program directory */
562       full_path = g_module_build_path(program_path, library_name);
563       full_path_w = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
564
565       if (full_path && full_path_w) {
566             dll_h = LoadLibraryW(full_path_w);
567             if (dll_h) {
568                   g_free(full_path);
569                   g_free(full_path_w);
570                   return dll_h;
571             }
572       }
573
574       /* Next try the system directory */
575       full_path = g_module_build_path(system_path, library_name);
576       full_path_w = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
577
578       if (full_path && full_path_w) {
579             dll_h = LoadLibraryW(full_path_w);
580             if (dll_h) {
581                   g_free(full_path);
582                   g_free(full_path_w);
583                   return dll_h;
584             }
585       }
586
587       return NULL;
588 }
589
590 GModule *
591 ws_module_open(gchar *module_name, GModuleFlags flags)
592 {
593       gchar   *full_path;
594       GModule *mod;
595
596       if (!init_dll_load_paths() || !module_name)
597             return NULL;
598
599       /* First try the program directory */
600       full_path = g_module_build_path(program_path, module_name);
601
602       if (full_path) {
603             mod = g_module_open(full_path, flags);
604             if (mod) {
605                   g_free(full_path);
606                   return mod;
607             }
608       }
609
610       /* Next try the system directory */
611       full_path = g_module_build_path(system_path, module_name);
612
613       if (full_path) {
614             mod = g_module_open(full_path, flags);
615             if (mod) {
616                   g_free(full_path);
617                   return mod;
618             }
619       }
620
621       /* At last try the Npcap directory */
622       full_path = g_module_build_path(npcap_path, module_name);
623
624       if (full_path) {
625             mod = g_module_open(full_path, flags);
626             if (mod) {
627                   g_free(full_path);
628                   return mod;
629             }
630       }
631
632       return NULL;
633 }
634
635 /** Create or open a "Wireshark is running" mutex.
636  */
637 #define WIRESHARK_IS_RUNNING_UUID "9CA78EEA-EA4D-4490-9240-FC01FCEF464B"
638
639 static SECURITY_ATTRIBUTES *sec_attributes_;
640
641 void create_app_running_mutex() {
642       SECURITY_ATTRIBUTES *sa = NULL;
643
644       if (!sec_attributes_) sec_attributes_ = g_new0(SECURITY_ATTRIBUTES, 1);
645
646       sec_attributes_->nLength = sizeof(SECURITY_ATTRIBUTES);
647       sec_attributes_->lpSecurityDescriptor = g_new0(SECURITY_DESCRIPTOR, 1);
648       sec_attributes_->bInheritHandle = TRUE;
649       if (InitializeSecurityDescriptor(sec_attributes_->lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) {
650             if (SetSecurityDescriptorDacl(sec_attributes_->lpSecurityDescriptor, TRUE, NULL, FALSE)) {
651                   sa = sec_attributes_;
652             }
653       }
654
655       if (!sa) {
656             g_free(sec_attributes_->lpSecurityDescriptor);
657             g_free(sec_attributes_);
658             sec_attributes_ = NULL;
659       }
660       CreateMutex(sa, FALSE, _T("Wireshark-is-running-{") _T(WIRESHARK_IS_RUNNING_UUID) _T("}"));
661       CreateMutex(sa, FALSE, _T("Global\\Wireshark-is-running-{") _T(WIRESHARK_IS_RUNNING_UUID) _T("}"));
662 }
663
664 /*
665  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
666  *
667  * Local variables:
668  * c-basic-offset: 6
669  * tab-width: 8
670  * indent-tabs-mode: nil
671  * End:
672  *
673  * vi: set shiftwidth=6 tabstop=8 expandtab:
674  * :indentSize=6:tabSize=8:noTabs=true:
675  */