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