file_util.h includes io.h, we don't need to do so ourselves.
[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
77 /**
78  * g_open:
79  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
80  * @flags: as in open()
81  * @mode: as in open()
82  *
83  * A wrapper for the POSIX open() function. The open() function is
84  * used to convert a pathname into a file descriptor. Note that on
85  * POSIX systems file descriptors are implemented by the operating
86  * system. On Windows, it's the C library that implements open() and
87  * file descriptors. The actual Windows API for opening files is
88  * something different.
89  *
90  * See the C library manual for more details about open().
91  *
92  * Returns: a new file descriptor, or -1 if an error occurred. The
93  * return value can be used exactly like the return value from open().
94  *
95  * Since: 2.6
96  */
97 int
98 ws_stdio_open (const gchar *filename,
99         int          flags,
100         int          mode)
101 {
102       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
103       int retval;
104       int save_errno;
105
106       if (wfilename == NULL)
107         {
108           errno = EINVAL;
109           return -1;
110         }
111
112       retval = _wopen (wfilename, flags, mode);
113       save_errno = errno;
114
115       g_free (wfilename);
116
117       errno = save_errno;
118       return retval;
119 }
120
121
122 /**
123  * g_rename:
124  * @oldfilename: a pathname in the GLib file name encoding (UTF-8 on Windows)
125  * @newfilename: a pathname in the GLib file name encoding
126  *
127  * A wrapper for the POSIX rename() function. The rename() function
128  * renames a file, moving it between directories if required.
129  *
130  * See your C library manual for more details about how rename() works
131  * on your system. Note in particular that on Win9x it is not possible
132  * to rename a file if a file with the new name already exists. Also
133  * it is not possible in general on Windows to rename an open file.
134  *
135  * Returns: 0 if the renaming succeeded, -1 if an error occurred
136  *
137  * Since: 2.6
138  */
139 int
140 ws_stdio_rename (const gchar *oldfilename,
141           const gchar *newfilename)
142 {
143       wchar_t *woldfilename = g_utf8_to_utf16 (oldfilename, -1, NULL, NULL, NULL);
144       wchar_t *wnewfilename;
145       int retval;
146       int save_errno = 0;
147
148       if (woldfilename == NULL)
149         {
150           errno = EINVAL;
151           return -1;
152         }
153
154       wnewfilename = g_utf8_to_utf16 (newfilename, -1, NULL, NULL, NULL);
155
156       if (wnewfilename == NULL)
157         {
158           g_free (woldfilename);
159           errno = EINVAL;
160           return -1;
161         }
162
163       if (MoveFileExW (woldfilename, wnewfilename, MOVEFILE_REPLACE_EXISTING))
164         retval = 0;
165       else
166         {
167           retval = -1;
168           switch (GetLastError ())
169             {
170 #define CASE(a,b) case ERROR_##a: save_errno = b; break
171             CASE (FILE_NOT_FOUND, ENOENT);
172             CASE (PATH_NOT_FOUND, ENOENT);
173             CASE (ACCESS_DENIED, EACCES);
174             CASE (NOT_SAME_DEVICE, EXDEV);
175             CASE (LOCK_VIOLATION, EACCES);
176             CASE (SHARING_VIOLATION, EACCES);
177             CASE (FILE_EXISTS, EEXIST);
178             CASE (ALREADY_EXISTS, EEXIST);
179 #undef CASE
180             default: save_errno = EIO;
181             }
182         }
183
184       g_free (woldfilename);
185       g_free (wnewfilename);
186
187       errno = save_errno;
188       return retval;
189 }
190
191 /**
192  * g_mkdir:
193  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
194  * @mode: permissions to use for the newly created directory
195  *
196  * A wrapper for the POSIX mkdir() function. The mkdir() function
197  * attempts to create a directory with the given name and permissions.
198  *
199  * See the C library manual for more details about mkdir().
200  *
201  * Returns: 0 if the directory was successfully created, -1 if an error
202  *    occurred
203  *
204  * Since: 2.6
205  */
206 int
207 ws_stdio_mkdir (const gchar *filename,
208          int          mode)
209 {
210       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
211       int retval;
212       int save_errno;
213
214       if (wfilename == NULL)
215         {
216           errno = EINVAL;
217           return -1;
218         }
219
220       retval = _wmkdir (wfilename);
221       save_errno = errno;
222
223       g_free (wfilename);
224
225       errno = save_errno;
226       return retval;
227 }
228
229 /**
230  * g_stat:
231  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
232  * @buf: a pointer to a <structname>stat</structname> struct, which
233  *    will be filled with the file information
234  *
235  * A wrapper for the POSIX stat() function. The stat() function
236  * returns information about a file.
237  *
238  * See the C library manual for more details about stat().
239  *
240  * Returns: 0 if the information was successfully retrieved, -1 if an error
241  *    occurred
242  *
243  * Since: 2.6
244  */
245 int
246 ws_stdio_stat64 (const gchar *filename,
247         ws_statb64 *buf)
248 {
249       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
250       int retval;
251       int save_errno;
252       size_t len;
253
254       if (wfilename == NULL)
255         {
256           errno = EINVAL;
257           return -1;
258         }
259
260       len = wcslen (wfilename);
261       while (len > 0 && G_IS_DIR_SEPARATOR (wfilename[len-1]))
262         len--;
263       if (len > 0 &&
264           (!g_path_is_absolute (filename) || len > (size_t) (g_path_skip_root (filename) - filename)))
265         wfilename[len] = '\0';
266
267       retval = _wstati64 (wfilename, buf);
268       save_errno = errno;
269
270       g_free (wfilename);
271
272       errno = save_errno;
273       return retval;
274 }
275 /**
276  * g_unlink:
277  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
278  *
279  * A wrapper for the POSIX unlink() function. The unlink() function
280  * deletes a name from the filesystem. If this was the last link to the
281  * file and no processes have it opened, the diskspace occupied by the
282  * file is freed.
283  *
284  * See your C library manual for more details about unlink(). Note
285  * that on Windows, it is in general not possible to delete files that
286  * are open to some process, or mapped into memory.
287  *
288  * Returns: 0 if the name was successfully deleted, -1 if an error
289  *    occurred
290  *
291  * Since: 2.6
292  */
293
294 int
295 ws_stdio_unlink (const gchar *filename)
296 {
297       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
298       int retval;
299       int save_errno;
300
301       if (wfilename == NULL)
302         {
303           errno = EINVAL;
304           return -1;
305         }
306
307       retval = _wunlink (wfilename);
308       save_errno = errno;
309
310       g_free (wfilename);
311
312       errno = save_errno;
313       return retval;
314 }
315
316 /**
317  * g_remove:
318  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
319  *
320  * A wrapper for the POSIX remove() function. The remove() function
321  * deletes a name from the filesystem.
322  *
323  * See your C library manual for more details about how remove() works
324  * on your system. On Unix, remove() removes also directories, as it
325  * calls unlink() for files and rmdir() for directories. On Windows,
326  * although remove() in the C library only works for files, this
327  * function tries first remove() and then if that fails rmdir(), and
328  * thus works for both files and directories. Note however, that on
329  * Windows, it is in general not possible to remove a file that is
330  * open to some process, or mapped into memory.
331  *
332  * If this function fails on Windows you can't infer too much from the
333  * errno value. rmdir() is tried regardless of what caused remove() to
334  * fail. Any errno value set by remove() will be overwritten by that
335  * set by rmdir().
336  *
337  * Returns: 0 if the file was successfully removed, -1 if an error
338  *    occurred
339  *
340  * Since: 2.6
341  */
342 int
343 ws_stdio_remove (const gchar *filename)
344 {
345       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
346       int retval;
347       int save_errno;
348
349       if (wfilename == NULL)
350         {
351           errno = EINVAL;
352           return -1;
353         }
354
355       retval = _wremove (wfilename);
356       if (retval == -1)
357         retval = _wrmdir (wfilename);
358       save_errno = errno;
359
360       g_free (wfilename);
361
362       errno = save_errno;
363       return retval;
364 }
365
366 /**
367  * g_fopen:
368  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
369  * @mode: a string describing the mode in which the file should be
370  *   opened
371  *
372  * A wrapper for the POSIX fopen() function. The fopen() function opens
373  * a file and associates a new stream with it.
374  *
375  * See the C library manual for more details about fopen().
376  *
377  * Returns: A <type>FILE</type> pointer if the file was successfully
378  *    opened, or %NULL if an error occurred
379  *
380  * Since: 2.6
381  */
382 FILE *
383 ws_stdio_fopen (const gchar *filename,
384          const gchar *mode)
385 {
386       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
387       wchar_t *wmode;
388       FILE *retval;
389       int save_errno;
390
391       if (wfilename == NULL)
392         {
393           errno = EINVAL;
394           return NULL;
395         }
396
397       wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
398
399       if (wmode == NULL)
400         {
401           g_free (wfilename);
402           errno = EINVAL;
403           return NULL;
404         }
405
406       retval = _wfopen (wfilename, wmode);
407       save_errno = errno;
408
409       g_free (wfilename);
410       g_free (wmode);
411
412       errno = save_errno;
413       return retval;
414 }
415
416 /**
417  * g_freopen:
418  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
419  * @mode: a string describing the mode in which the file should be
420  *   opened
421  * @stream: an existing stream which will be reused, or %NULL
422  *
423  * A wrapper for the POSIX freopen() function. The freopen() function
424  * opens a file and associates it with an existing stream.
425  *
426  * See the C library manual for more details about freopen().
427  *
428  * Returns: A <type>FILE</type> pointer if the file was successfully
429  *    opened, or %NULL if an error occurred.
430  *
431  * Since: 2.6
432  */
433 FILE *
434 ws_stdio_freopen (const gchar *filename,
435            const gchar *mode,
436            FILE        *stream)
437 {
438       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
439       wchar_t *wmode;
440       FILE *retval;
441       int save_errno;
442
443       if (wfilename == NULL)
444         {
445           errno = EINVAL;
446           return NULL;
447         }
448
449       wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
450
451       if (wmode == NULL)
452         {
453           g_free (wfilename);
454           errno = EINVAL;
455           return NULL;
456         }
457
458       retval = _wfreopen (wfilename, wmode, stream);
459       save_errno = errno;
460
461       g_free (wfilename);
462       g_free (wmode);
463
464       errno = save_errno;
465       return retval;
466 }
467
468
469 /* DLL loading */
470 static gboolean
471 init_dll_load_paths()
472 {
473       TCHAR path_w[MAX_PATH];
474
475       if (program_path && system_path)
476             return TRUE;
477
478       /* XXX - Duplicate code in filesystem.c:init_progfile_dir */
479       if (GetModuleFileName(NULL, path_w, MAX_PATH) == 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
480             return FALSE;
481       }
482
483       if (!program_path) {
484             gchar *app_path;
485             app_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
486             /* We could use PathRemoveFileSpec here but we'd have to link to Shlwapi.dll */
487             program_path = g_path_get_dirname(app_path);
488             g_free(app_path);
489       }
490
491       if (GetSystemDirectory(path_w, MAX_PATH) == 0) {
492             return FALSE;
493       }
494
495       if (!system_path) {
496             system_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
497       }
498
499       if (program_path && system_path)
500             return TRUE;
501
502       return FALSE;
503 }
504
505 gboolean
506 ws_init_dll_search_path()
507 {
508       gboolean dll_dir_set = FALSE;
509       wchar_t *program_path_w;
510
511       typedef BOOL (WINAPI *SetDllDirectoryHandler)(LPCTSTR);
512       SetDllDirectoryHandler PSetDllDirectory;
513
514       PSetDllDirectory = (SetDllDirectoryHandler) GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "SetDllDirectoryW");
515       if (PSetDllDirectory) {
516             dll_dir_set = PSetDllDirectory(_T(""));
517       }
518
519       if (!dll_dir_set && init_dll_load_paths()) {
520             program_path_w = g_utf8_to_utf16(program_path, -1, NULL, NULL, NULL);
521             SetCurrentDirectory(program_path_w);
522             g_free(program_path_w);
523       }
524
525       return dll_dir_set;
526 }
527
528 /*
529  * Internally g_module_open uses LoadLibrary on Windows and returns an
530  * HMODULE cast to a GModule *. However there's no guarantee that this
531  * will always be the case, so we call LoadLibrary and g_module_open
532  * separately.
533  */
534
535 void *
536 ws_load_library(gchar *library_name)
537 {
538       gchar   *full_path;
539       wchar_t *full_path_w;
540       HMODULE  dll_h;
541
542       if (!init_dll_load_paths() || !library_name)
543             return NULL;
544
545       /* First try the program directory */
546       full_path = g_module_build_path(program_path, library_name);
547       full_path_w = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
548
549       if (full_path && full_path_w) {
550             dll_h = LoadLibraryW(full_path_w);
551             if (dll_h) {
552                   g_free(full_path);
553                   g_free(full_path_w);
554                   return dll_h;
555             }
556       }
557
558       /* Next try the system directory */
559       full_path = g_module_build_path(system_path, library_name);
560       full_path_w = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
561
562       if (full_path && full_path_w) {
563             dll_h = LoadLibraryW(full_path_w);
564             if (dll_h) {
565                   g_free(full_path);
566                   g_free(full_path_w);
567                   return dll_h;
568             }
569       }
570
571       return NULL;
572 }
573
574 GModule *
575 ws_module_open(gchar *module_name, GModuleFlags flags)
576 {
577       gchar   *full_path;
578       GModule *mod;
579
580       if (!init_dll_load_paths() || !module_name)
581             return NULL;
582
583       /* First try the program directory */
584       full_path = g_module_build_path(program_path, module_name);
585
586       if (full_path) {
587             mod = g_module_open(full_path, flags);
588             if (mod) {
589                   g_free(full_path);
590                   return mod;
591             }
592       }
593
594       /* Next try the system directory */
595       full_path = g_module_build_path(system_path, module_name);
596
597       if (full_path) {
598             mod = g_module_open(full_path, flags);
599             if (mod) {
600                   g_free(full_path);
601                   return mod;
602             }
603       }
604
605       return NULL;
606 }
607
608 /* utf8 version of getenv, needed to get win32 filename paths */
609 char *
610 getenv_utf8(const char *varname)
611 {
612         char *envvar;
613         wchar_t *envvarw;
614         wchar_t *varnamew;
615
616         envvar = getenv(varname);
617
618         /* since GLib 2.6 we need an utf8 version of the filename */
619         /* using the wide char version of getenv should work under all circumstances */
620
621         /* convert given varname to utf16, needed by _wgetenv */
622         varnamew = g_utf8_to_utf16(varname, -1, NULL, NULL, NULL);
623         if (varnamew == NULL) {
624                 return envvar;
625         }
626
627         /* use wide char version of getenv */
628         envvarw = _wgetenv(varnamew);
629         g_free(varnamew);
630         if (envvarw == NULL) {
631                 return envvar;
632         }
633
634         /* convert value to utf8 */
635         envvar = g_utf16_to_utf8(envvarw, -1, NULL, NULL, NULL);
636         /* XXX - memleak */
637
638         return envvar;
639 }
640
641 /** Create or open a "Wireshark is running" mutex.
642  */
643 #define WIRESHARK_IS_RUNNING_UUID "9CA78EEA-EA4D-4490-9240-FC01FCEF464B"
644
645 static SECURITY_ATTRIBUTES *sec_attributes_;
646
647 void create_app_running_mutex() {
648       SECURITY_ATTRIBUTES *sa = NULL;
649
650       if (!sec_attributes_) sec_attributes_ = g_new0(SECURITY_ATTRIBUTES, 1);
651
652       sec_attributes_->nLength = sizeof(SECURITY_ATTRIBUTES);
653       sec_attributes_->lpSecurityDescriptor = g_new0(SECURITY_DESCRIPTOR, 1);
654       sec_attributes_->bInheritHandle = TRUE;
655       if (InitializeSecurityDescriptor(sec_attributes_->lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) {
656             if (SetSecurityDescriptorDacl(sec_attributes_->lpSecurityDescriptor, TRUE, NULL, FALSE)) {
657                   sa = sec_attributes_;
658             }
659       }
660
661       if (!sa) {
662             g_free(sec_attributes_->lpSecurityDescriptor);
663             g_free(sec_attributes_);
664             sec_attributes_ = NULL;
665       }
666       CreateMutex(sa, FALSE, _T("Wireshark-is-running-{") _T(WIRESHARK_IS_RUNNING_UUID) _T("}"));
667       CreateMutex(sa, FALSE, _T("Global\\Wireshark-is-running-{") _T(WIRESHARK_IS_RUNNING_UUID) _T("}"));
668 }
669
670 /*
671  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
672  *
673  * Local variables:
674  * c-basic-offset: 6
675  * tab-width: 8
676  * indent-tabs-mode: nil
677  * End:
678  *
679  * vi: set shiftwidth=6 tabstop=8 expandtab:
680  * :indentSize=6:tabSize=8:noTabs=true:
681  */