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