Added dissection of some packages Property ID:s
[obnox/wireshark/wip.git] / epan / filesystem.c
1 /* filesystem.c
2  * Filesystem utility routines
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include <glib.h>
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #ifdef HAVE_SYS_STAT_H
41 #include <sys/stat.h>
42 #endif
43
44 #ifdef HAVE_WINDOWS_H
45 #include <windows.h>
46 #endif
47
48 #ifdef HAVE_DIRECT_H
49 #include <direct.h>             /* to declare "mkdir()" on Windows */
50 #endif
51
52 #ifndef _WIN32
53 #include <pwd.h>
54 #endif
55
56 #include "filesystem.h"
57
58 /*
59  * Given a pathname, return a pointer to the last pathname separator
60  * character in the pathname, or NULL if the pathname contains no
61  * separators.
62  */
63 char *
64 find_last_pathname_separator(char *path)
65 {
66         char *separator;
67
68 #ifdef _WIN32
69         char c;
70
71         /*
72          * We have to scan for '\' or '/'.
73          * Get to the end of the string.
74          */
75         separator = path + strlen(path);        /* points to ending '\0' */
76         while (separator > path) {
77                 c = *--separator;
78                 if (c == '\\' || c == '/')
79                         return separator;       /* found it */
80         }
81
82         /*
83          * OK, we didn't find any, so no directories - but there might
84          * be a drive letter....
85          */
86         return strchr(path, ':');
87 #else
88         separator = strrchr(path, '/');
89 #endif
90         return separator;
91 }
92
93 /*
94  * Given a pathname, return the last component.
95  */
96 char *
97 get_basename(char *path)
98 {
99         char *filename;
100
101         g_assert(path != NULL);
102         filename = find_last_pathname_separator(path);
103         if (filename == NULL) {
104                 /*
105                  * There're no directories, drive letters, etc. in the
106                  * name; the pathname *is* the file name.
107                  */
108                 filename = path;
109         } else {
110                 /*
111                  * Skip past the pathname or drive letter separator.
112                  */
113                 filename++;
114         }
115         return filename;
116 }
117
118 /*
119  * Given a pathname, return a string containing everything but the
120  * last component.  NOTE: this overwrites the pathname handed into
121  * it....
122  */
123 char *
124 get_dirname(char *path)
125 {
126         char *separator;
127
128         g_assert(path != NULL);
129         separator = find_last_pathname_separator(path);
130         if (separator == NULL) {
131                 /*
132                  * There're no directories, drive letters, etc. in the
133                  * name; there is no directory path to return.
134                  */
135                 return NULL;
136         }
137
138         /*
139          * Get rid of the last pathname separator and the final file
140          * name following it.
141          */
142         *separator = '\0';
143
144         /*
145          * "path" now contains the pathname of the directory containing
146          * the file/directory to which it referred.
147          */
148         return path;
149 }
150
151 /*
152  * Given a pathname, return:
153  *
154  *      the errno, if an attempt to "stat()" the file fails;
155  *
156  *      EISDIR, if the attempt succeeded and the file turned out
157  *      to be a directory;
158  *
159  *      0, if the attempt succeeded and the file turned out not
160  *      to be a directory.
161  */
162
163 /*
164  * Visual C++ on Win32 systems doesn't define these.  (Old UNIX systems don't
165  * define them either.)
166  *
167  * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
168  */
169 #ifndef S_ISREG
170 #define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
171 #endif
172 #ifndef S_IFIFO
173 #define S_IFIFO _S_IFIFO
174 #endif
175 #ifndef S_ISFIFO
176 #define S_ISFIFO(mode)  (((mode) & S_IFMT) == S_IFIFO)
177 #endif
178 #ifndef S_ISDIR
179 #define S_ISDIR(mode)   (((mode) & S_IFMT) == S_IFDIR)
180 #endif
181
182 int
183 test_for_directory(const char *path)
184 {
185         struct stat statb;
186
187         if (stat(path, &statb) < 0)
188                 return errno;
189
190         if (S_ISDIR(statb.st_mode))
191                 return EISDIR;
192         else
193                 return 0;
194 }
195
196 int
197 test_for_fifo(const char *path)
198 {
199         struct stat statb;
200
201         if (stat(path, &statb) < 0)
202                 return errno;
203
204         if (S_ISFIFO(statb.st_mode))
205                 return ESPIPE;
206         else
207                 return 0;
208 }
209
210 /*
211  * Get the directory in which Ethereal's global configuration and data
212  * files are stored.
213  *
214  * XXX - if we ever make libethereal a real library, used by multiple
215  * applications (more than just Tethereal and versions of Ethereal with
216  * various UIs), should the configuration files belong to the library
217  * (and be shared by all those applications) or to the applications?
218  *
219  * If they belong to the library, that could be done on UNIX by the
220  * configure script, but it's trickier on Windows, as you can't just
221  * use the pathname of the executable.
222  *
223  * If they belong to the application, that could be done on Windows
224  * by using the pathname of the executable, but we'd have to have it
225  * passed in as an argument, in some call, on UNIX.
226  *
227  * Note that some of those configuration files might be used by code in
228  * libethereal, some of them might be used by dissectors (would they
229  * belong to libethereal, the application, or a separate library?),
230  * and some of them might be used by other code (the Ethereal preferences
231  * file includes resolver preferences that control the behavior of code
232  * in libethereal, dissector preferences, and UI preferences, for
233  * example).
234  */
235 const char *
236 get_datafile_dir(void)
237 {
238 #ifdef _WIN32
239         char prog_pathname[_MAX_PATH+2];
240         char *dir_end;
241         size_t datafile_dir_len;
242         static char *datafile_dir;
243
244         /*
245          * Have we already gotten the pathname?
246          * If so, just return it.
247          */
248         if (datafile_dir != NULL)
249                 return datafile_dir;
250
251         /*
252          * No, we haven't.
253          * Start out by assuming it's the default installation directory.
254          */
255         datafile_dir = "C:\\Program Files\\Ethereal\\";
256
257         /*
258          * Now we attempt to get the full pathname of the currently running
259          * program, under the assumption that we're running an installed
260          * version of the program.  If we fail, we don't change "datafile_dir",
261          * and thus end up using the default.
262          *
263          * XXX - does NSIS put the installation directory into
264          * "\HKEY_LOCAL_MACHINE\SOFTWARE\Ethereal\InstallDir"?
265          * If so, perhaps we should read that from the registry,
266          * instead.
267          */
268         if (GetModuleFileName(NULL, prog_pathname, sizeof prog_pathname) != 0) {
269                 /*
270                  * If the program is an installed version, the full pathname
271                  * includes the pathname of the directory in which it was
272                  * installed; get that directory's pathname, and construct
273                  * from it the pathname of the directory in which the
274                  * plugins were installed.
275                  *
276                  * First, find the last "\\" in the directory, as that
277                  * marks the end of the directory pathname.
278                  *
279                  * XXX - Can the pathname be something such as
280                  * "C:ethereal.exe"?  Or is it always a full pathname
281                  * beginning with "\\" after the drive letter?
282                  */
283                 dir_end = strrchr(prog_pathname, '\\');
284                 if (dir_end != NULL) {
285                         /*
286                          * Found it - now figure out how long the datafile
287                          * directory pathname will be.
288                          */
289                         datafile_dir_len = (dir_end - prog_pathname);
290
291                         /*
292                          * Allocate a buffer for the plugin directory
293                          * pathname, and construct it.
294                          */
295                         datafile_dir = g_malloc(datafile_dir_len + 1);
296                         strncpy(datafile_dir, prog_pathname, datafile_dir_len);
297                         datafile_dir[datafile_dir_len] = '\0';
298                 }
299         }
300         return datafile_dir;
301 #else
302         /*
303          * Just use DATAFILE_DIR, as that's what the configure script
304          * set it to be.
305          */
306         return DATAFILE_DIR;
307 #endif
308 }
309
310 /*
311  * Get the directory in which files that, at least on UNIX, are
312  * system files (such as "/etc/ethers") are stored; on Windows,
313  * there's no "/etc" directory, so we get them from the Ethereal
314  * global configuration and data file directory.
315  */
316 const char *
317 get_systemfile_dir(void)
318 {
319 #ifdef _WIN32
320         return get_datafile_dir();
321 #else
322         return "/etc";
323 #endif
324 }
325
326 /*
327  * Name of directory, under the user's home directory, in which
328  * personal configuration files are stored.
329  */
330 #ifdef _WIN32
331 #define PF_DIR "Ethereal"
332 #else
333 /*
334  * XXX - should this be ".libepan"? For backwards-compatibility, I'll keep
335  * it ".ethereal" for now.
336  */
337 #define PF_DIR ".ethereal"
338 #endif
339
340 /*
341  * Get the directory in which personal configuration files reside;
342  * in UNIX-compatible systems, it's ".ethereal", under the user's home
343  * directory, and on Windows systems, it's "Ethereal", under %APPDATA%
344  * or, if %APPDATA% isn't set, it's "%USERPROFILE%\Application Data"
345  * (which is what %APPDATA% normally is on Windows 2000).
346  */
347 static const char *
348 get_persconffile_dir(void)
349 {
350 #ifdef _WIN32
351         char *appdatadir;
352         char *userprofiledir;
353 #else
354         char *homedir;
355         struct passwd *pwd;
356 #endif
357         static char *pf_dir = NULL;
358
359         /* Return the cached value, if available */
360         if (pf_dir != NULL)
361                 return pf_dir;
362
363 #ifdef _WIN32
364         /*
365          * Use %APPDATA% or %USERPROFILE%, so that configuration files are
366          * stored in the user profile, rather than in the home directory.
367          * The Windows convention is to store configuration information
368          * in the user profile, and doing so means you can use
369          * Ethereal even if the home directory is an inaccessible
370          * network drive.
371          */
372         appdatadir = getenv("APPDATA");
373         if (appdatadir != NULL) {
374                 /*
375                  * Concatenate %APPDATA% with "\Ethereal".
376                  */
377                 pf_dir = g_malloc(strlen(appdatadir) + strlen(PF_DIR) + 2);
378                 sprintf(pf_dir, "%s" G_DIR_SEPARATOR_S "%s", appdatadir,
379                     PF_DIR);
380         } else {
381                 /*
382                  * OK, %APPDATA% wasn't set, so use
383                  * %USERPROFILE%\Application Data.
384                  */
385                 userprofiledir = getenv("USERPROFILE");
386                 if (userprofiledir != NULL) {
387                         pf_dir = g_malloc(strlen(userprofiledir) +
388                             strlen("Application Data") + strlen(PF_DIR) + 3);
389                         sprintf(pf_dir,
390                             "%s" G_DIR_SEPARATOR_S "Application Data" G_DIR_SEPARATOR_S "%s",
391                             userprofiledir, PF_DIR);
392                 } else {
393                         /*
394                          * Give up and use "C:".
395                          */
396                         pf_dir = g_malloc(strlen("C:") + strlen(PF_DIR) + 2);
397                         sprintf(pf_dir, "C:" G_DIR_SEPARATOR_S "%s", PF_DIR);
398                 }
399         }
400 #else
401         /*
402          * If $HOME is set, use that.
403          */
404         homedir = getenv("HOME");
405         if (homedir == NULL) {
406                 /*
407                  * Get their home directory from the password file.
408                  * If we can't even find a password file entry for them,
409                  * use "/tmp".
410                  */
411                 pwd = getpwuid(getuid());
412                 if (pwd != NULL) {
413                         /*
414                          * This is cached, so we don't need to worry
415                          * about allocating multiple ones of them.
416                          */
417                         homedir = g_strdup(pwd->pw_dir);
418                 } else
419                         homedir = "/tmp";
420         }
421         pf_dir = g_malloc(strlen(homedir) + strlen(PF_DIR) + 2);
422         sprintf(pf_dir, "%s" G_DIR_SEPARATOR_S "%s", homedir, PF_DIR);
423 #endif
424
425         return pf_dir;
426 }
427
428 /*
429  * Create the directory that holds personal configuration files, if
430  * necessary.  If we attempted to create it, and failed, return -1 and
431  * set "*pf_dir_path_return" to the pathname of the directory we failed
432  * to create (it's g_mallocated, so our caller should free it); otherwise,
433  * return 0.
434  */
435 int
436 create_persconffile_dir(char **pf_dir_path_return)
437 {
438         const char *pf_dir_path;
439 #ifdef _WIN32
440         char *pf_dir_path_copy, *pf_dir_parent_path;
441         size_t pf_dir_parent_path_len;
442 #endif
443         struct stat s_buf;
444         int ret;
445
446         pf_dir_path = get_persconffile_dir();
447         if (stat(pf_dir_path, &s_buf) != 0 && errno == ENOENT) {
448 #ifdef _WIN32
449                 /*
450                  * Does the parent directory of that directory
451                  * exist?  %APPDATA% may not exist even though
452                  * %USERPROFILE% does.
453                  *
454                  * We check for the existence of the directory
455                  * by first checking whether the parent directory
456                  * is just a drive letter and, if it's not, by
457                  * doing a "stat()" on it.  If it's a drive letter,
458                  * or if the "stat()" succeeds, we assume it exists.
459                  */
460                 pf_dir_path_copy = g_strdup(pf_dir_path);
461                 pf_dir_parent_path = get_dirname(pf_dir_path_copy);
462                 pf_dir_parent_path_len = strlen(pf_dir_parent_path);
463                 if (pf_dir_parent_path_len > 0
464                     && pf_dir_parent_path[pf_dir_parent_path_len - 1] != ':'
465                     && stat(pf_dir_parent_path, &s_buf) != 0) {
466                         /*
467                          * No, it doesn't exist - make it first.
468                          */
469                         ret = mkdir(pf_dir_parent_path);
470                         if (ret == -1) {
471                                 *pf_dir_path_return = pf_dir_parent_path;
472                                 return -1;
473                         }
474                 }
475                 g_free(pf_dir_path_copy);
476                 ret = mkdir(pf_dir_path);
477 #else
478                 ret = mkdir(pf_dir_path, 0755);
479 #endif
480         } else {
481                 /*
482                  * Something with that pathname exists; if it's not
483                  * a directory, we'll get an error if we try to put
484                  * something in it, so we don't fail here, we wait
485                  * for that attempt fo fail.
486                  */
487                 ret = 0;
488         }
489         if (ret == -1)
490                 *pf_dir_path_return = g_strdup(pf_dir_path);
491         return ret;
492 }
493
494 #ifdef _WIN32
495 /*
496  * Returns the user's home directory on Win32.
497  */
498 static const char *
499 get_home_dir(void)
500 {
501         static const char *home = NULL;
502         char *homedrive, *homepath;
503         char *homestring;
504         char *lastsep;
505
506         /* Return the cached value, if available */
507         if (home)
508                 return home;
509
510         /*
511          * XXX - should we use USERPROFILE anywhere in this process?
512          * Is there a chance that it might be set but one or more of
513          * HOMEDRIVE or HOMEPATH isn't set?
514          */
515         homedrive = getenv("HOMEDRIVE");
516         if (homedrive != NULL) {
517                 homepath = getenv("HOMEPATH");
518                 if (homepath != NULL) {
519                         /*
520                          * This is cached, so we don't need to worry about
521                          * allocating multiple ones of them.
522                          */
523                         homestring =
524                             g_malloc(strlen(homedrive) + strlen(homepath) + 1);
525                         strcpy(homestring, homedrive);
526                         strcat(homestring, homepath);
527
528                         /*
529                          * Trim off any trailing slash or backslash.
530                          */
531                         lastsep = find_last_pathname_separator(homestring);
532                         if (lastsep != NULL && *(lastsep + 1) == '\0') {
533                                 /*
534                                  * Last separator is the last character
535                                  * in the string.  Nuke it.
536                                  */
537                                 *lastsep = '\0';
538                         }
539                         home = homestring;
540                 } else
541                         home = homedrive;
542         } else {
543                 /*
544                  * Give up and use C:.
545                  */
546                 home = "C:";
547         }
548
549         return home;
550 }
551 #endif
552
553 /*
554  * Construct the path name of a personal configuration file, given the
555  * file name.
556  *
557  * On Win32, if "for_writing" is FALSE, we check whether the file exists
558  * and, if not, construct a path name relative to the ".ethereal"
559  * subdirectory of the user's home directory, and check whether that
560  * exists; if it does, we return that, so that configuration files
561  * from earlier versions can be read.
562  */
563 char *
564 get_persconffile_path(const char *filename, gboolean for_writing
565 #ifndef _WIN32
566         _U_
567 #endif
568 )
569 {
570         char *path;
571 #ifdef _WIN32
572         struct stat s_buf;
573         char *old_path;
574 #endif
575
576         path = (gchar *) g_malloc(strlen(get_persconffile_dir()) +
577             strlen(filename) + 2);
578         sprintf(path, "%s" G_DIR_SEPARATOR_S "%s", get_persconffile_dir(),
579             filename);
580 #ifdef _WIN32
581         if (!for_writing) {
582                 if (stat(path, &s_buf) != 0 && errno == ENOENT) {
583                         /*
584                          * OK, it's not in the personal configuration file
585                          * directory; is it in the ".ethereal" subdirectory
586                          * of their home directory?
587                          */
588                         old_path = (gchar *) g_malloc(strlen(get_home_dir()) +
589                             strlen(".ethereal") + strlen(filename) + 3);
590                         sprintf(old_path,
591                             "%s" G_DIR_SEPARATOR_S ".ethereal" G_DIR_SEPARATOR_S "%s",
592                             get_home_dir(), filename);
593                         if (stat(old_path, &s_buf) == 0) {
594                                 /*
595                                  * OK, it exists; return it instead.
596                                  */
597                                 g_free(path);
598                                 path = old_path;
599                         }
600                 }
601         }
602 #endif
603
604         return path;
605 }
606
607 /*
608  * Construct the path name of a global configuration file, given the
609  * file name.
610  */
611 char *
612 get_datafile_path(const char *filename)
613 {
614         char *path;
615
616         path = (gchar *) g_malloc(strlen(get_datafile_dir()) +
617             strlen(filename) + 2);
618         sprintf(path, "%s" G_DIR_SEPARATOR_S "%s", get_datafile_dir(),
619             filename);
620
621         return path;
622 }
623
624 /* Delete a file */
625 gboolean
626 deletefile(const char *path)
627 {
628         return unlink(path) == 0;
629 }
630
631 /*
632  * Construct and return the path name of a file in the
633  * appropriate temporary file directory.
634  */
635 char *get_tempfile_path(const char *filename)
636 {
637         char *path;
638
639         path = (gchar *) g_malloc(strlen(g_get_tmp_dir()) +
640             strlen(filename) + 2);
641         sprintf(path, "%s" G_DIR_SEPARATOR_S "%s", g_get_tmp_dir(), filename);
642
643         return path;
644 }
645
646 /*
647  * Return an error message for UNIX-style errno indications on open or
648  * create operations.
649  */
650 char *
651 file_open_error_message(int err, gboolean for_writing)
652 {
653         char *errmsg;
654         static char errmsg_errno[1024+1];
655
656         switch (err) {
657
658         case ENOENT:
659                 if (for_writing)
660                         errmsg = "The path to the file \"%s\" does not exist.";
661                 else
662                         errmsg = "The file \"%s\" does not exist.";
663                 break;
664
665         case EACCES:
666                 if (for_writing)
667                         errmsg = "You do not have permission to create or write to the file \"%s\".";
668                 else
669                         errmsg = "You do not have permission to read the file \"%s\".";
670                 break;
671
672         case EISDIR:
673                 errmsg = "\"%s\" is a directory (folder), not a file.";
674                 break;
675
676         case ENOSPC:
677                 errmsg = "The file \"%s\" could not be created because there is no space left on the file system.";
678                 break;
679
680 #ifdef EDQUOT
681         case EDQUOT:
682                 errmsg = "The file \"%s\" could not be created because you are too close to, or over, your disk quota.";
683                 break;
684 #endif
685
686         default:
687                 snprintf(errmsg_errno, sizeof(errmsg_errno),
688                                 "The file \"%%s\" could not be %s: %s.",
689                                 for_writing ? "created" : "opened",
690                                 strerror(err));
691                 errmsg = errmsg_errno;
692                 break;
693         }
694         return errmsg;
695 }
696
697 /*
698  * Return an error message for UNIX-style errno indications on write
699  * operations.
700  */
701 char *
702 file_write_error_message(int err)
703 {
704         char *errmsg;
705         static char errmsg_errno[1024+1];
706
707         switch (err) {
708
709         case ENOSPC:
710                 errmsg = "The file \"%s\" could not be saved because there is no space left on the file system.";
711                 break;
712
713 #ifdef EDQUOT
714         case EDQUOT:
715                 errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota.";
716                 break;
717 #endif
718
719         default:
720                 snprintf(errmsg_errno, sizeof(errmsg_errno),
721                     "An error occurred while writing to the file \"%%s\": %s.",
722                     strerror(err));
723                 errmsg = errmsg_errno;
724                 break;
725         }
726         return errmsg;
727 }
728