0b7acc1f4a3a65f80f9c312891103c6085ff2bec
[openafs.git] / src / util / dirpath.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <stddef.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <limits.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include "assert.h"
24 #include "afsutil.h"
25 #include "fileutil.h"
26 #ifdef AFS_PTHREAD_ENV
27 #include <pthread.h>
28 static pthread_once_t dirInit_once = PTHREAD_ONCE_INIT;
29 #endif
30 #ifdef AFS_NT40_ENV
31 #include <windows.h>
32 #include <WINNT\afssw.h>
33 #endif
34 #ifdef AFS_DARWIN_ENV
35 #include <unistd.h>
36 #endif
37
38 /* local vars */
39 /* static storage for path strings */
40 static char dirPathArray[AFSDIR_PATHSTRING_MAX][AFSDIR_PATH_MAX];
41
42 /* indicate if and how the dirpath module initialized. */
43 static int initFlag = 0;
44 static unsigned int initStatus = 0;
45
46
47 /* storage for dynamically-determined install dir (NT only; long and short) */
48 #ifdef AFS_NT40_ENV
49 static char ntServerInstallDirLong[AFSDIR_PATH_MAX];
50 static char ntServerInstallDirShort[AFSDIR_PATH_MAX];
51 static char ntClientConfigDirLong[AFSDIR_PATH_MAX];
52 static char ntClientConfigDirShort[AFSDIR_PATH_MAX];
53 #endif
54
55 /* storage for local afs server/client paths (top-level) */
56 static char afsSrvDirPath[AFSDIR_PATH_MAX];
57 static char afsClntDirPath[AFSDIR_PATH_MAX];
58
59 /* internal array init function */
60 static void initDirPathArray(void);
61
62 /* Additional macros for ease of use */
63 /* buf is expected to be atleast AFS_PATH_MAX bytes long */
64 #define AFSDIR_SERVER_DIRPATH(buf, dir)  \
65             (void) strcompose(buf, AFSDIR_PATH_MAX, serverPrefix, dir, NULL)
66
67 #define AFSDIR_SERVER_FILEPATH(buf, dir, file)  \
68             (void) strcompose(buf, AFSDIR_PATH_MAX, serverPrefix, dir, "/", file,  NULL)
69
70 #define AFSDIR_CLIENT_DIRPATH(buf, dir)  \
71             (void) strcompose(buf, AFSDIR_PATH_MAX, clientPrefix, dir, NULL)
72
73 #define AFSDIR_CLIENT_FILEPATH(buf, dir, file)  \
74             (void) strcompose(buf, AFSDIR_PATH_MAX,  clientPrefix, dir, "/", file,  NULL)
75
76
77 /* initAFSDirPath() -- External users call this function to initialize
78  * the dirpath module and/or to determine the initialization status.
79  */
80 unsigned int
81 initAFSDirPath(void)
82 {
83     if (initFlag == 0) {        /* not yet init'ed, so initialize */
84 #ifdef AFS_PTHREAD_ENV
85         pthread_once(&dirInit_once, initDirPathArray);
86 #else
87         initDirPathArray();
88 #endif
89     }
90     return initStatus;
91 }
92
93
94 /* initDirPathArray() -- Initializes the afs dir paths for the 
95  *     server and client installations.
96  *
97  *     For NT these are determined dynamically; for Unix they are static.
98  *
99  *     NT NOTE: If a particular component (client/server) is not installed
100  *              then we may not be able to initialize the paths to anything
101  *              meaningful.  In this case the paths are set to the local
102  *              temp directory to avoid later reference to an uninitialized
103  *              variable.  The initStatus flag is set to indicate which
104  *              paths (client/server) initialized properly for callers of
105  *              initAFSDirPath() who would like to know this information.
106  */
107 static void
108 initDirPathArray(void)
109 {
110     char *pathp;
111     const char *clientPrefix = "";
112     const char *serverPrefix = "";
113
114 #ifdef AFS_NT40_ENV
115     char *buf;
116     int status;
117
118     /* get the afs server software installation dir from the registry */
119     if (afssw_GetServerInstallDir(&buf)) {
120         /* failed; server not installed; use temp directory */
121         strcpy(ntServerInstallDirLong, gettmpdir());
122     } else {
123         strcpy(ntServerInstallDirLong, buf);
124         free(buf);
125         initStatus |= AFSDIR_SERVER_PATHS_OK;
126     }
127     FilepathNormalize(ntServerInstallDirLong);
128     status =
129         GetShortPathName(ntServerInstallDirLong, ntServerInstallDirShort,
130                          AFSDIR_PATH_MAX);
131     if (status == 0 || status > AFSDIR_PATH_MAX) {
132         /* can't convert path to short version; just use long version */
133         strcpy(ntServerInstallDirShort, ntServerInstallDirLong);
134     }
135     FilepathNormalize(ntServerInstallDirShort);
136
137     /* get the afs client configuration directory (/usr/vice/etc equivalent) */
138     status = GetWindowsDirectory(ntClientConfigDirLong, AFSDIR_PATH_MAX);
139     if (status == 0 || status > AFSDIR_PATH_MAX) {
140         /* failed to get canonical Windows directory; use temp directory */
141         strcpy(ntClientConfigDirLong, gettmpdir());
142     } else {
143         initStatus |= AFSDIR_CLIENT_PATHS_OK;
144     }
145     FilepathNormalize(ntClientConfigDirLong);
146
147     status =
148         GetShortPathName(ntClientConfigDirLong, ntClientConfigDirShort,
149                          AFSDIR_PATH_MAX);
150     if (status == 0 || status > AFSDIR_PATH_MAX) {
151         /* can't convert path to short version; just use long version */
152         strcpy(ntClientConfigDirShort, ntClientConfigDirLong);
153     }
154     FilepathNormalize(ntClientConfigDirShort);
155     clientPrefix = ntClientConfigDirShort;
156
157     /* setup the root server directory path (/usr/afs equivalent) */
158     strcpy(afsSrvDirPath, ntServerInstallDirShort);
159     strcat(afsSrvDirPath, AFSDIR_CANONICAL_SERVER_AFS_DIRPATH);
160
161     /* there is no root client directory path (/usr/vice equivalent) */
162     afsClntDirPath[0] = '\0';
163
164     /* setup top level dirpath (/usr equivalent); valid for server ONLY */
165     strcpy(dirPathArray[AFSDIR_USR_DIRPATH_ID], ntServerInstallDirShort);
166     serverPrefix = ntServerInstallDirShort;
167     strcat(dirPathArray[AFSDIR_USR_DIRPATH_ID], AFSDIR_CANONICAL_USR_DIRPATH);
168
169 #else /* AFS_NT40_ENV */
170     /* setup the root server directory path */
171     strcpy(afsSrvDirPath, AFSDIR_CANONICAL_SERVER_AFS_DIRPATH);
172
173     /* setup the root client directory path */
174 #ifdef AFS_DARWIN_ENV
175     if (access(AFSDIR_ALTERNATE_CLIENT_VICE_DIRPATH, F_OK) == 0)
176         strcpy(afsClntDirPath, AFSDIR_ALTERNATE_CLIENT_VICE_DIRPATH);
177     else
178 #endif
179         strcpy(afsClntDirPath, AFSDIR_CANONICAL_CLIENT_VICE_DIRPATH);
180
181     /* setup top level dirpath; valid for both server and client */
182     strcpy(dirPathArray[AFSDIR_USR_DIRPATH_ID], AFSDIR_CANONICAL_USR_DIRPATH);
183
184     initStatus |= (AFSDIR_CLIENT_PATHS_OK | AFSDIR_SERVER_PATHS_OK);
185 #endif /* AFS_NT40_ENV */
186
187     /* now initialize various dir and file paths exported by dirpath module */
188
189     /* server dir paths */
190
191     strcpy(dirPathArray[AFSDIR_SERVER_AFS_DIRPATH_ID], afsSrvDirPath);
192
193     pathp = dirPathArray[AFSDIR_SERVER_ETC_DIRPATH_ID];
194     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_SERVER_ETC_DIR);
195
196     pathp = dirPathArray[AFSDIR_SERVER_BIN_DIRPATH_ID];
197     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_SERVER_BIN_DIR);
198
199     pathp = dirPathArray[AFSDIR_SERVER_CORES_DIRPATH_ID];
200     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_CORES_DIR);
201
202     pathp = dirPathArray[AFSDIR_SERVER_DB_DIRPATH_ID];
203     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_DB_DIR);
204
205     pathp = dirPathArray[AFSDIR_SERVER_LOGS_DIRPATH_ID];
206     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_LOGS_DIR);
207
208     pathp = dirPathArray[AFSDIR_SERVER_LOCAL_DIRPATH_ID];
209     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_LOCAL_DIR);
210
211     pathp = dirPathArray[AFSDIR_SERVER_BACKUP_DIRPATH_ID];
212     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_BACKUP_DIR);
213
214     pathp = dirPathArray[AFSDIR_SERVER_MIGRATE_DIRPATH_ID];
215     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_MIGR_DIR);
216
217     pathp = dirPathArray[AFSDIR_SERVER_BIN_FILE_DIRPATH_ID];
218     AFSDIR_SERVER_DIRPATH(pathp, AFSDIR_BIN_FILE_DIR);
219
220     /* client dir path */
221
222 #ifdef AFS_NT40_ENV
223     strcpy(dirPathArray[AFSDIR_CLIENT_VICE_DIRPATH_ID],
224            "/NoUsrViceDirectoryOnWindows");
225     strcpy(dirPathArray[AFSDIR_CLIENT_ETC_DIRPATH_ID],
226            ntClientConfigDirShort);
227 #else
228     strcpy(dirPathArray[AFSDIR_CLIENT_VICE_DIRPATH_ID], afsClntDirPath);
229
230     pathp = dirPathArray[AFSDIR_CLIENT_ETC_DIRPATH_ID];
231 #ifdef AFS_DARWIN_ENV
232     if (access(AFSDIR_ALTERNATE_CLIENT_ETC_DIR, F_OK) == 0)
233         AFSDIR_CLIENT_DIRPATH(pathp, AFSDIR_ALTERNATE_CLIENT_ETC_DIR);
234     else
235 #endif
236         AFSDIR_CLIENT_DIRPATH(pathp, AFSDIR_CLIENT_ETC_DIR);
237 #endif /* AFS_NT40_ENV */
238
239     /* server file paths */
240
241     pathp = dirPathArray[AFSDIR_SERVER_THISCELL_FILEPATH_ID];
242     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_ETC_DIR,
243                            AFSDIR_THISCELL_FILE);
244
245     pathp = dirPathArray[AFSDIR_SERVER_CELLSERVDB_FILEPATH_ID];
246     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_ETC_DIR,
247                            AFSDIR_CELLSERVDB_FILE);
248
249     pathp = dirPathArray[AFSDIR_SERVER_NOAUTH_FILEPATH_ID];
250     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_NOAUTH_FILE);
251
252     pathp = dirPathArray[AFSDIR_SERVER_BUDBLOG_FILEPATH_ID];
253     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_BUDBLOG_FILE);
254
255     pathp = dirPathArray[AFSDIR_SERVER_TAPECONFIG_FILEPATH_ID];
256     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BACKUP_DIR, AFSDIR_TAPECONFIG_FILE);
257
258     pathp = dirPathArray[AFSDIR_SERVER_KALOGDB_FILEPATH_ID];
259     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_KALOGDB_FILE);
260
261     pathp = dirPathArray[AFSDIR_SERVER_KALOG_FILEPATH_ID];
262     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_KALOG_FILE);
263
264     pathp = dirPathArray[AFSDIR_SERVER_KADB_FILEPATH_ID];
265     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_DB_DIR, AFSDIR_KADB_FILE);
266
267     pathp = dirPathArray[AFSDIR_SERVER_NTPD_FILEPATH_ID];
268     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_BIN_DIR, AFSDIR_NTPD_FILE);
269
270     pathp = dirPathArray[AFSDIR_SERVER_PRDB_FILEPATH_ID];
271     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_DB_DIR, AFSDIR_PRDB_FILE);
272
273     pathp = dirPathArray[AFSDIR_SERVER_PTLOG_FILEPATH_ID];
274     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_PTLOG_FILE);
275
276     pathp = dirPathArray[AFSDIR_SERVER_KCONF_FILEPATH_ID];
277     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_ETC_DIR, AFSDIR_KCONF_FILE);
278
279     pathp = dirPathArray[AFSDIR_SERVER_VLDB_FILEPATH_ID];
280     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_DB_DIR, AFSDIR_VLDB_FILE);
281
282     pathp = dirPathArray[AFSDIR_SERVER_VLOG_FILEPATH_ID];
283     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_VLOG_FILE);
284
285     pathp = dirPathArray[AFSDIR_SERVER_CORELOG_FILEPATH_ID];
286     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_CORE_FILE);
287
288     pathp = dirPathArray[AFSDIR_SERVER_SLVGLOG_FILEPATH_ID];
289     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_SLVGLOG_FILE);
290
291     pathp = dirPathArray[AFSDIR_SERVER_SALVAGER_FILEPATH_ID];
292     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_BIN_DIR,
293                            AFSDIR_SALVAGER_FILE);
294
295     pathp = dirPathArray[AFSDIR_SERVER_SLVGLOCK_FILEPATH_ID];
296     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_SLVGLOCK_FILE);
297
298     pathp = dirPathArray[AFSDIR_SERVER_KEY_FILEPATH_ID];
299     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_ETC_DIR, AFSDIR_KEY_FILE);
300
301     pathp = dirPathArray[AFSDIR_SERVER_ULIST_FILEPATH_ID];
302     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_ETC_DIR, AFSDIR_ULIST_FILE);
303
304     pathp = dirPathArray[AFSDIR_SERVER_BOZCONF_FILEPATH_ID];
305     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BOSCONFIG_DIR, AFSDIR_BOZCONF_FILE);
306
307     pathp = dirPathArray[AFSDIR_SERVER_BOZCONFNEW_FILEPATH_ID];
308     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BOSCONFIG_DIR,
309                            AFSDIR_BOZCONFNEW_FILE);
310
311     pathp = dirPathArray[AFSDIR_SERVER_BOZLOG_FILEPATH_ID];
312     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_BOZLOG_FILE);
313
314     pathp = dirPathArray[AFSDIR_SERVER_BOZINIT_FILEPATH_ID];
315     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BOSCONFIG_DIR, AFSDIR_BOZINIT_FILE);
316
317     pathp = dirPathArray[AFSDIR_SERVER_BOSVR_FILEPATH_ID];
318     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_BOSSERVER_DIR, AFSDIR_BOSVR_FILE);
319
320     pathp = dirPathArray[AFSDIR_SERVER_VOLSERLOG_FILEPATH_ID];
321     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_VOLSERLOG_FILE);
322
323     pathp = dirPathArray[AFSDIR_SERVER_ROOTVOL_FILEPATH_ID];
324     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_SERVER_ETC_DIR, AFSDIR_ROOTVOL_FILE);
325
326     pathp = dirPathArray[AFSDIR_SERVER_HOSTDUMP_FILEPATH_ID];
327     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_HOSTDUMP_FILE);
328
329     pathp = dirPathArray[AFSDIR_SERVER_CLNTDUMP_FILEPATH_ID];
330     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_CLNTDUMP_FILE);
331
332     pathp = dirPathArray[AFSDIR_SERVER_CBKDUMP_FILEPATH_ID];
333     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_CBKDUMP_FILE);
334
335     pathp = dirPathArray[AFSDIR_SERVER_OLDSYSID_FILEPATH_ID];
336     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_OLDSYSID_FILE);
337
338     pathp = dirPathArray[AFSDIR_SERVER_SYSID_FILEPATH_ID];
339     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_SYSID_FILE);
340
341     pathp = dirPathArray[AFSDIR_SERVER_FILELOG_FILEPATH_ID];
342     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOGS_DIR, AFSDIR_FILELOG_FILE);
343
344     pathp = dirPathArray[AFSDIR_SERVER_AUDIT_FILEPATH_ID];
345     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_AUDIT_FILE);
346
347     pathp = dirPathArray[AFSDIR_SERVER_NETINFO_FILEPATH_ID];
348     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_NETINFO_FILE);
349
350     pathp = dirPathArray[AFSDIR_SERVER_NETRESTRICT_FILEPATH_ID];
351     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_LOCAL_DIR, AFSDIR_NETRESTRICT_FILE);
352
353     pathp = dirPathArray[AFSDIR_SERVER_WEIGHTING_CONSTANTS_FILEPATH_ID];
354     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_MIGR_DIR,
355                            AFSDIR_WEIGHTINGCONST_FILE);
356
357     pathp = dirPathArray[AFSDIR_SERVER_THRESHOLD_CONSTANTS_FILEPATH_ID];
358     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_MIGR_DIR,
359                            AFSDIR_THRESHOLDCONST_FILE);
360
361     pathp = dirPathArray[AFSDIR_SERVER_MIGRATELOG_FILEPATH_ID];
362     AFSDIR_SERVER_FILEPATH(pathp, AFSDIR_MIGR_DIR, AFSDIR_MIGRATE_LOGNAME);
363
364
365     /* client file paths */
366
367 #ifdef AFS_NT40_ENV
368     strcpy(dirPathArray[AFSDIR_CLIENT_THISCELL_FILEPATH_ID],
369            "/NoUsrViceEtcThisCellFileOnWindows");
370     sprintf(dirPathArray[AFSDIR_CLIENT_CELLSERVDB_FILEPATH_ID], "%s/%s",
371             ntClientConfigDirShort, AFSDIR_CELLSERVDB_FILE_NTCLIENT);
372     strcpy(dirPathArray[AFSDIR_CLIENT_CELLALIAS_FILEPATH_ID],
373            "/NoCellAliasOnWindows");
374 #else
375     pathp = dirPathArray[AFSDIR_CLIENT_THISCELL_FILEPATH_ID];
376     AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_CLIENT_ETC_DIR,
377                            AFSDIR_THISCELL_FILE);
378
379     pathp = dirPathArray[AFSDIR_CLIENT_CELLSERVDB_FILEPATH_ID];
380     AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_CLIENT_ETC_DIR,
381                            AFSDIR_CELLSERVDB_FILE);
382
383     pathp = dirPathArray[AFSDIR_CLIENT_CELLALIAS_FILEPATH_ID];
384     AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_CLIENT_ETC_DIR,
385                            AFSDIR_CELLALIAS_FILE);
386 #endif /* AFS_NT40_ENV */
387
388     pathp = dirPathArray[AFSDIR_CLIENT_NETINFO_FILEPATH_ID];
389     AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_CLIENT_ETC_DIR, AFSDIR_NETINFO_FILE);
390
391     pathp = dirPathArray[AFSDIR_CLIENT_NETRESTRICT_FILEPATH_ID];
392     AFSDIR_CLIENT_FILEPATH(pathp, AFSDIR_CLIENT_ETC_DIR,
393                            AFSDIR_NETRESTRICT_FILE);
394
395     initFlag = 1;               /* finished dirpath initialization */
396     return;
397 }
398
399 /* getDirPath - returns a const char pointer to the requested string
400  * from the internal path array.
401  * string_id - index into the path array 
402  */
403 const char *
404 getDirPath(afsdir_id_t string_id)
405 {
406     /* check if the array has been initialized */
407     if (initFlag == 0) {        /* no it's not, so initialize */
408 #ifdef AFS_PTHREAD_ENV
409         pthread_once(&dirInit_once, initDirPathArray);
410 #else
411         initDirPathArray();
412 #endif
413     }
414     return (const char *)dirPathArray[string_id];
415 }
416
417 /*
418  * LocalizePathHead() -- Make path relative to local part
419  *
420  * ConstructLocalPath takes a path  and a directory that path should
421  * be considered relative to.   This  function checks the given path
422  * for   a prefix  that represents a canonical path.  If such a prefix
423  * is found,  the path is adjusted to remove the prefix and the path
424  * is considered  relative to the local version of that path.
425  */
426
427 /* The following array  maps cannonical parts to local parts.  It
428  * might  seem reasonable to  simply construct an array in parallel to
429  * dirpatharray  but it turns out you don't want translations for all
430  local paths.
431 */
432
433 struct canonmapping {
434     const char *canonical;
435     const char *local;
436 };
437 static struct canonmapping CanonicalTranslations[] = {
438     {AFSDIR_CANONICAL_SERVER_ETC_DIRPATH, AFSDIR_SERVER_ETC_DIR},
439     {AFSDIR_CANONICAL_SERVER_LOGS_DIRPATH, AFSDIR_LOGS_DIR},
440     {AFSDIR_CANONICAL_SERVER_LOCAL_DIRPATH, AFSDIR_LOCAL_DIR},
441     {AFSDIR_CANONICAL_SERVER_BIN_DIRPATH, AFSDIR_SERVER_BIN_DIR},
442     {NULL, NULL}
443 };
444
445 static void
446 LocalizePathHead(const char **path, const char **relativeTo)
447 {
448     struct canonmapping *current;
449     for (current = CanonicalTranslations; current->local != NULL; current++) {
450         int canonlength = strlen(current->canonical);
451         if (strncmp(*path, current->canonical, canonlength) == 0) {
452             (*path) += canonlength;
453             if (**path == '/')
454                 (*path)++;
455             *relativeTo = current->local;
456             return;
457         }
458     }
459 }
460
461
462 #ifdef AFS_NT40_ENV
463 /* NT version of ConstructLocalPath() */
464
465 /*
466  * ConstructLocalPath() --  Convert a canonical (wire-format) path to a fully
467  *     specified local path.  Upon successful completion, *fullPathBufp is
468  *     set to an allocated buffer containing the fully specified local path
469  *     constructed from the cpath argument.
470  *
471  *     On NT, path construction proceeds as follows:
472  *         1) If cpath is fully qualified (i.e., starts with 'X:/') then the
473  *            path returned is equivalent to cpath.
474  *         2) If cpath begins with a drive letter but is not fully qualified,
475  *            i.e., it is drive relative, then the function fails with EINVAL.
476  *         3) If cpath begins with '/' (or '\') then the path returned is the
477  *            concatenation  AFS-server-install-dir + cpath after translating for localization.
478  *         4) Otherwise the path returned is the concatenation
479  *            AFS-server-install-dir + relativeTo + cpath.
480  *
481  *     Leading whitespace in cpath is ignored; the constructed path is
482  *     normalized (FilepathNormalize()).
483  *
484  * RETURN CODES: 0 if successful; errno code otherwise.
485  */
486 int
487 ConstructLocalPath(const char *cpath, const char *relativeTo,
488                    char **fullPathBufp)
489 {
490     int status = 0;
491     char *newPath = NULL;
492
493     if (initFlag == 0) {        /* dirpath module not yet initialized */
494 #ifdef AFS_PTHREAD_ENV
495         pthread_once(&dirInit_once, initDirPathArray);
496 #else
497         initDirPathArray();
498 #endif
499     }
500
501     *fullPathBufp = NULL;
502
503     while (isspace(*cpath)) {
504         cpath++;
505     }
506
507     LocalizePathHead(&cpath, &relativeTo);
508     if ((((*cpath >= 'a') && (*cpath <= 'z'))
509          || ((*cpath >= 'A') && (*cpath <= 'Z'))) && (*(cpath + 1) == ':')) {
510
511         /* cpath has a leading drive letter */
512         if ((*(cpath + 2) != '/') && (*(cpath + 2) != '\\')) {
513             /* drive letter relative path; this is not allowed */
514             status = EINVAL;
515         } else {
516             /* fully qualified path; just make a copy */
517             newPath = (char *)malloc(strlen(cpath) + 1);
518             if (!newPath) {
519                 status = ENOMEM;
520             } else {
521                 (void)strcpy(newPath, cpath);
522             }
523         }
524
525     } else {
526         /* cpath has NO leading drive letter; make relative to install dir */
527         size_t pathSize = strlen(ntServerInstallDirShort) + 2;
528
529         if ((*cpath == '/') || (*cpath == '\\')) {
530             /* construct path relative to install directory only */
531             pathSize += strlen(cpath);
532
533             newPath = (char *)malloc(pathSize);
534             if (!newPath) {
535                 status = ENOMEM;
536             } else {
537                 sprintf(newPath, "%s/%s", ntServerInstallDirShort, cpath);
538             }
539         } else {
540             /* construct path relative to 'relativeTo' (and install dir) */
541             pathSize += strlen(relativeTo) + 1 + strlen(cpath);
542
543             newPath = (char *)malloc(pathSize);
544             if (!newPath) {
545                 status = ENOMEM;
546             } else {
547                 sprintf(newPath, "%s/%s/%s", ntServerInstallDirShort,
548                         relativeTo, cpath);
549             }
550         }
551     }
552
553     if (status == 0) {
554         FilepathNormalize(newPath);
555
556         /* return buffer containing fully specified path */
557         *fullPathBufp = newPath;
558     }
559
560     return status;
561 }
562
563 #else
564 /* Unix version of ConstructLocalPath() */
565
566 /*
567  * ConstructLocalPath() --  Convert a canonical (wire-format) path to a fully
568  *     specified local path.  Upon successful completion, *fullPathBufp is
569  *     set to an allocated buffer containing the fully specified local path
570  *     constructed from the cpath argument.
571  *
572  *     On Unix, path construction proceeds as follows:
573  *         1) If cpath begins with '/' then the path returned is equivalent
574  *            to cpath.
575  *         2) Otherwise the path returned is the concatenation
576  *            relativeTo + cpath.
577  *
578  *     Leading whitespace in cpath is ignored; the constructed path is
579  *     normalized (FilepathNormalize()).
580  *
581  * RETURN CODES: 0 if successful; errno code otherwise.
582  */
583 int
584 ConstructLocalPath(const char *cpath, const char *relativeTo,
585                    char **fullPathBufp)
586 {
587     int status = 0;
588     char *newPath = NULL;
589
590     if (initFlag == 0) {        /* dirpath module not yet initialized */
591 #ifdef AFS_PTHREAD_ENV
592         pthread_once(&dirInit_once, initDirPathArray);
593 #else
594         initDirPathArray();
595 #endif
596     }
597
598     *fullPathBufp = NULL;
599
600     while (isspace(*cpath)) {
601         cpath++;
602     }
603
604     LocalizePathHead(&cpath, &relativeTo);
605     if (*cpath == '/') {
606         newPath = (char *)malloc(strlen(cpath) + 1);
607         if (!newPath) {
608             status = ENOMEM;
609         } else {
610             strcpy(newPath, cpath);
611         }
612     } else {
613         newPath = (char *)malloc(strlen(relativeTo) + 1 + strlen(cpath) + 1);
614         if (!newPath) {
615             status = ENOMEM;
616         } else {
617             sprintf(newPath, "%s/%s", relativeTo, cpath);
618         }
619     }
620
621     if (status == 0) {
622         FilepathNormalize(newPath);
623
624         /* return buffer containing fully specified path */
625         *fullPathBufp = newPath;
626     }
627
628     return status;
629 }
630 #endif /* AFS_NT40_ENV */
631
632
633 /*
634  * ConstructLocalBinPath() -- A convenience wrapper for ConstructLocalPath()
635  *     that specifies the canonical AFS server binary directory as the relative
636  *     directory.
637  */
638 int
639 ConstructLocalBinPath(const char *cpath, char **fullPathBufp)
640 {
641     return ConstructLocalPath(cpath, AFSDIR_CANONICAL_SERVER_BIN_DIRPATH,
642                               fullPathBufp);
643 }
644
645
646 /*
647  * ConstructLocalLogPath() -- A convenience wrapper for ConstructLocalPath()
648  *     that specifies the canonical AFS server logs directory as the relative
649  *     directory.
650  */
651 int
652 ConstructLocalLogPath(const char *cpath, char **fullPathBufp)
653 {
654     return ConstructLocalPath(cpath, AFSDIR_CANONICAL_SERVER_LOGS_DIRPATH,
655                               fullPathBufp);
656 }