jbeuhler-flexelint-bugs-found-20031128
[openafs.git] / src / update / server.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 <afs/stds.h>
17 #ifdef  AFS_AIX32_ENV
18 #include <signal.h>
19 #endif
20 #include <sys/types.h>
21 #ifdef AFS_NT40_ENV
22 #include <winsock2.h>
23 #include <WINNT/afsevent.h>
24 #include <fcntl.h>
25 #include <io.h>
26 #include <afs/dirent.h>
27 #include <afs/procmgmt.h>
28 #else
29 #include <netdb.h>
30 #include <netinet/in.h>
31 #include <sys/file.h>
32 #include <dirent.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #else
37 #ifdef HAVE_STRINGS_H
38 #include <strings.h>
39 #endif
40 #endif
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #include <sys/stat.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <rx/xdr.h>
48 #include <rx/rx.h>
49 #include <rx/rxkad.h>
50 #include <afs/cellconfig.h>
51 #include <afs/afsutil.h>
52 #include <afs/fileutil.h>
53 #include "update.h"
54 #include "global.h"
55
56 extern int UPDATE_ExecuteRequest();
57 static int AddObject(char **expPath, char *dir);
58 static int PathInDirectory(char *dir, char *path);
59
60 struct afsconf_dir *cdir;
61 int nDirs;
62 char *dirName[MAXENTRIES];
63 int dirLevel[MAXENTRIES];
64 char *whoami;
65
66 static int Quit();
67
68 /* check whether caller is authorized to manage RX statistics */
69 int
70 update_rxstat_userok(call)
71      struct rx_call *call;
72 {
73     return afsconf_SuperUser(cdir, call, NULL);
74 }
75
76 /*
77  * PathInDirectory() -- determine if path is in directory (or is directory)
78  */
79 static int
80 PathInDirectory(char *dir, char *path)
81 {
82     int inDir = 0;
83     size_t dirLen;
84     char dirNorm[AFSDIR_PATH_MAX], pathNorm[AFSDIR_PATH_MAX];
85
86 #ifdef AFS_NT40_ENV
87     /* case-insensitive comparison of normalized, same-flavor (short) paths */
88     DWORD status;
89
90     status = GetShortPathName(dir, dirNorm, AFSDIR_PATH_MAX);
91     if (status == 0 || status > AFSDIR_PATH_MAX) {
92         /* can't convert path to short version; just use long version */
93         strcpy(dirNorm, dir);
94     }
95     FilepathNormalize(dirNorm);
96
97     status = GetShortPathName(path, pathNorm, AFSDIR_PATH_MAX);
98     if (status == 0 || status > AFSDIR_PATH_MAX) {
99         /* can't convert path to short version; just use long version */
100         strcpy(pathNorm, path);
101     }
102     FilepathNormalize(pathNorm);
103
104     dirLen = strlen(dirNorm);
105
106     if (_strnicmp(dirNorm, pathNorm, dirLen) == 0) {
107         /* substrings match; path must match dir or be subdirectory */
108         if (pathNorm[dirLen] == '\0' || pathNorm[dirLen] == '/') {
109             inDir = 1;
110         }
111     }
112 #else
113     /* case-sensitive comparison of normalized paths */
114     strcpy(dirNorm, dir);
115     FilepathNormalize(dirNorm);
116
117     strcpy(pathNorm, path);
118     FilepathNormalize(pathNorm);
119
120     dirLen = strlen(dirNorm);
121
122     if (strncmp(dirNorm, pathNorm, dirLen) == 0) {
123         /* substrings match; path must match dir or be subdirectory */
124         if (pathNorm[dirLen] == '\0' || pathNorm[dirLen] == '/') {
125             inDir = 1;
126         }
127     }
128 #endif /* AFS_NT40_ENV */
129     return inDir;
130 }
131
132
133 int
134 AuthOkay(call, name)
135      struct rx_call *call;
136      char *name;
137 {
138     int i;
139     rxkad_level level;
140     afs_int32 code;
141     int matches;
142
143     /* Must be in 'UserList' to use */
144     if (!afsconf_SuperUser(cdir, call, NULL))
145         return 0;
146
147     if (rx_SecurityClassOf(rx_ConnectionOf(call)) == 2) {
148         code = rxkad_GetServerInfo(call->conn, &level, 0, 0, 0, 0, 0);
149         if (code)
150             return 0;
151     } else
152         level = 0;
153
154     matches = 0;
155     for (i = 0; i < nDirs; i++) {
156         if (PathInDirectory(dirName[i], name)) {
157             if (dirLevel[i] > level)
158                 return 0;
159             matches++;
160             /* keep searching in case there's a more restrictive subtree
161              * specified later. */
162         }
163     }
164     if (nDirs && !matches)
165         return 0;               /* if dirs spec., name must match */
166     return 1;                   /* okay or no dirs */
167 }
168
169 int
170 osi_audit()
171 {
172 /* this sucks but it works for now.
173 */
174     return 0;
175 }
176
177 #ifndef AFS_NT40_ENV
178 #include "AFS_component_version_number.c"
179 #endif
180
181 int
182 main(argc, argv)
183      int argc;
184      char *argv[];
185 {
186     struct rx_securityClass *securityObjects[3];
187     struct rx_service *service;
188
189     int a = 0;
190     rxkad_level level;
191     rxkad_level newLevel;
192
193 #ifdef  AFS_AIX32_ENV
194     /*
195      * The following signal action for AIX is necessary so that in case of a 
196      * crash (i.e. core is generated) we can include the user's data section 
197      * in the core dump. Unfortunately, by default, only a partial core is
198      * generated which, in many cases, isn't too useful.
199      */
200     struct sigaction nsa;
201
202     sigemptyset(&nsa.sa_mask);
203     nsa.sa_handler = SIG_DFL;
204     nsa.sa_flags = SA_FULLDUMP;
205     sigaction(SIGABRT, &nsa, NULL);
206     sigaction(SIGSEGV, &nsa, NULL);
207 #endif
208
209     whoami = argv[0];
210
211 #ifdef AFS_NT40_ENV
212     /* dummy signal call to force afsprocmgmt.dll to load on NT */
213     signal(SIGUSR1, SIG_DFL);
214 #endif
215
216     /* Initialize dirpaths */
217     if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
218 #ifdef AFS_NT40_ENV
219         ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
220 #endif
221         fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
222                 argv[0]);
223         exit(2);
224     }
225     nDirs = 0;
226     level = rxkad_clear;
227
228     if (argc == 1)              /* no arguments */
229         goto usage;
230
231     /* support help flag */
232     if (strcmp("-help", argv[1]) == 0)
233         goto usage;
234     if (strcmp("help", argv[1]) == 0)
235         goto usage;
236
237     for (a = 1; a < argc; a++) {
238         if (argv[a][0] == '-') {        /* parse options */
239             char arg[256];
240             lcstring(arg, argv[a], sizeof(arg));
241             newLevel = StringToLevel(&argv[a][1]);
242             if (newLevel != -1) {
243                 level = newLevel;       /* set new level */
244                 continue;
245             }
246           usage:
247             Quit("Usage: upserver [<directory>+] [-crypt <directory>+] [-clear <directory>+] [-auth <directory>+] [-help]\n");
248         } else {
249             int dirlen;
250             if (nDirs >= sizeof(dirName) / sizeof(dirName[0]))
251                 Quit("Too many dirs");
252             dirlen = strlen(argv[a]);
253             if (AddObject(&dirName[nDirs], argv[a])) {
254                 printf("%s: Unable to export dir %s. Skipping\n", whoami,
255                        argv[a]);
256                 continue;
257             }
258             dirLevel[nDirs] = level;    /* remember current level */
259             nDirs++;
260         }
261     }
262
263     if (nDirs == 0) {           /* Didn't find any directories to export */
264         printf("%s: No directories to export. Quitting\n", whoami);
265         exit(1);
266     }
267
268     cdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
269     if (cdir == 0) {
270         fprintf(stderr, "Can't get server configuration info (%s)\n",
271                 AFSDIR_SERVER_ETC_DIRPATH);
272         exit(1);
273     }
274
275     /* Initialize Rx, telling it port number this server will use for its
276      * single service */
277     if (rx_Init(htons(AFSCONF_UPDATEPORT)) < 0)
278         Quit("rx_init");
279
280     /* Create a single security object, in this case the null security object,
281      * for unauthenticated connections, which will be used to control security
282      * on connections made to this server. */
283
284     /* WHAT A JOKE!  Let's use rxkad at least so we know who we're talking to,
285      * then sometimes require full encryption. */
286
287     /* rxnull and rxvab are no longer supported */
288     securityObjects[0] = rxnull_NewServerSecurityObject();
289
290     securityObjects[1] = (struct rx_securityClass *)0;
291
292     securityObjects[2] =
293         rxkad_NewServerSecurityObject(rxkad_clear, cdir, afsconf_GetKey, 0);
294     if (securityObjects[2] == (struct rx_securityClass *)0)
295         Quit("rxkad_NewServerSecurityObject");
296
297     /* Instantiate a single UPDATE service.  The rxgen-generated procedure
298      * which is called to decode requests is passed in here
299      * (UPDATE_ExecuteRequest). */
300     service =
301         rx_NewService(0, UPDATE_SERVICEID, "UPDATE", securityObjects, 3,
302                       UPDATE_ExecuteRequest);
303     if (service == (struct rx_service *)0)
304         Quit("rx_NewService");
305     rx_SetMaxProcs(service, 2);
306
307     /* allow super users to manage RX statistics */
308     rx_SetRxStatUserOk(update_rxstat_userok);
309
310     rx_StartServer(1);          /* Donate this process to the server process pool */
311     Quit("StartServer returned?");
312     return 0;
313 }
314
315 /* fetch the file name and send it to the remote requester specified by call */
316
317 int
318 UPDATE_FetchFile(call, name)
319      struct rx_call *call;
320      char *name;
321 {
322     int fd = -1;
323     int error = 0;
324     struct stat status;
325     char *reqObject;
326
327     /* construct a local path from a canonical (wire-format) path */
328     if ((error = ConstructLocalPath(name, "/", &reqObject))) {
329         com_err(whoami, error, "Unable to construct local path");
330         return UPDATE_ERROR;
331     }
332
333     if (!AuthOkay(call, reqObject)) {
334         error = UPDATE_ERROR;
335     } else {
336         fd = open(reqObject, O_RDONLY, 0);
337         if (fd < 0 || fstat(fd, &status) < 0) {
338             printf("Failed to open %s\n", reqObject);
339             error = UPDATE_ERROR;
340         }
341         if (!error)
342             error = update_SendFile(fd, call, &status);
343         if (fd >= 0)
344             close(fd);
345     }
346     free(reqObject);
347     return error;
348 }
349
350 /* fetch dir info about directory name and send it to remote host associated
351   with call. */
352 int
353 UPDATE_FetchInfo(call, name)
354      struct rx_call *call;
355      char *name;
356 {
357     int error = 0;
358     struct stat status;
359     char *reqObject;
360
361     /* construct a local path from a canonical (wire-format) path */
362     if ((error = ConstructLocalPath(name, "/", &reqObject))) {
363         com_err(whoami, error, "Unable to construct local path");
364         return UPDATE_ERROR;
365     }
366
367     if (!AuthOkay(call, reqObject)) {
368         error = UPDATE_ERROR;
369     } else {
370         /* we only need to stat the obj, not open it. */
371         if (stat(reqObject, &status) < 0) {
372             printf("Failed to open %s\n", reqObject);
373             error = UPDATE_ERROR;
374         }
375         if ((status.st_mode & S_IFMT) != S_IFDIR) {
376             printf(" file %s is not a directory \n", reqObject);
377             error = -1;
378         }
379
380         if (!error)
381             error = update_SendDirInfo(reqObject, call, &status, name);
382     }
383     free(reqObject);
384     return error;
385 }
386
387 static int
388 Quit(msg, a, b)
389      char *msg;
390 {
391     fprintf(stderr, msg, a, b);
392     exit(1);
393 }
394
395 int
396 update_SendFile(fd, call, status)
397      register int fd;
398      register struct rx_call *call;
399      register struct stat *status;
400 {
401     char *buffer = (char *)0;
402     int blockSize;
403     afs_int32 length, tlen;
404 #ifdef notdef
405     XDR xdr;
406 #endif
407 #ifdef  AFS_AIX_ENV
408 #include <sys/statfs.h>
409     struct statfs tstatfs;
410 #endif
411
412     afs_int32 error = 0;
413 #ifdef  AFS_AIX_ENV
414     /* Unfortunately in AIX valuable fields such as st_blksize are gone from the stat structure!! */
415     fstatfs(fd, &tstatfs);
416     blockSize = tstatfs.f_bsize;
417 #elif AFS_NT40_ENV
418     blockSize = 4096;
419 #else
420     blockSize = status->st_blksize;
421 #endif
422     length = status->st_size;
423     buffer = (char *)malloc(blockSize);
424     if (!buffer) {
425         printf("malloc failed\n");
426         return UPDATE_ERROR;
427     }
428 #ifdef  notdef
429     xdrrx_create(&xdr, call, XDR_ENCODE);
430     if (!xdr_afs_int32(&xdr, &length))
431         error = UPDATE_ERROR;
432 #else
433     tlen = htonl(length);
434     rx_Write(call, &tlen, sizeof(afs_int32));   /* send length on fetch */
435 #endif
436     while (!error && length) {
437         register int nbytes = (length > blockSize ? blockSize : length);
438         nbytes = read(fd, buffer, nbytes);
439         if (nbytes <= 0) {
440             fprintf(stderr, "File system read failed\n");
441             break;
442         }
443         if (rx_Write(call, buffer, nbytes) != nbytes)
444             break;
445         length -= nbytes;
446     }
447     if (buffer)
448         free(buffer);
449     if (length)
450         error = UPDATE_ERROR;
451     return error;
452 }
453
454 /* Enumerate dir (name) and write dir entry info into temp file. 
455  */
456 int
457 update_SendDirInfo(name, call, status, origDir)
458      char *name;                /* Name of dir to enumerate */
459      register struct rx_call *call;     /* rx call */
460      register struct stat *status;      /* stat struct for dir */
461      char *origDir;             /* orig name of dir before being localized */
462 {
463     DIR *dirp;
464     struct dirent *dp;
465     FILE *stream;
466     struct stat tstatus;
467     char filename[MAXSIZE], dirInfoFile[MAXSIZE];
468     int fd, tfd, errcode, error, err;
469
470     error = 0;
471     dirp = opendir(name);
472     sprintf(dirInfoFile, "%s/upserver.tmp", gettmpdir());
473     stream = fopen(dirInfoFile, "w");
474     if (!stream) {
475         error = EIO;
476     } else {
477         while ((dp = readdir(dirp))) {
478             strcpy(filename, name);
479             strcat(filename, "/");
480             strcat(filename, dp->d_name);
481
482             tfd = open(filename, O_RDONLY, 0);
483             if (tfd < 0 || fstat(tfd, &tstatus) < 0) {
484                 printf("Failed to open %s\n", name);
485                 error = UPDATE_ERROR;
486                 goto fail;
487             }
488             if ((tstatus.st_mode & S_IFMT) != S_IFDIR) {        /* not a directory */
489                 char dirEntry[MAXSIZE];
490
491                 strcpy(dirEntry, origDir);
492                 strcat(dirEntry, "/");
493                 strcat(dirEntry, dp->d_name);
494                 err =
495                     fprintf(stream, "\"%s\" %u %u %u %u %u %u\n", dirEntry,
496                             (unsigned int)tstatus.st_mtime,
497                             (unsigned int)tstatus.st_size, tstatus.st_mode,
498                             tstatus.st_uid, tstatus.st_gid,
499                             (unsigned int)tstatus.st_atime);
500                 if (err < 0)
501                     error = EIO;
502             }
503             err = close(tfd);
504             if (err) {
505                 printf("could not close file %s \n", filename);
506                 error = UPDATE_ERROR;
507                 goto fail;
508             }
509         }
510     }
511   fail:
512     if (dirp)
513         closedir(dirp);
514     if (stream) {
515         if (ferror(stream))
516             if (!error)
517                 error = UPDATE_ERROR;
518         fclose(stream);
519     }
520     if (error == 0) {
521         fd = open(dirInfoFile, O_RDONLY, 0);
522         if (fd >= 0) {
523             fstat(fd, &tstatus);
524             errcode = update_SendFile(fd, call, &tstatus);
525             if (errcode)
526                 if (!error)
527                     error = UPDATE_ERROR;
528             close(fd);
529         }
530     }
531     unlink(dirInfoFile);
532     return error;
533 }
534
535
536 /* AddObject() - Adds the object to the list of exported objects after
537  *     converting to a local path.
538  *
539  * expPath : points to allocated storage in which the exportable path is 
540  *           passed back.
541  * dir     : dir name passed in for export 
542  *
543  */
544 static int
545 AddObject(char **expPath, char *dir)
546 {
547     int error;
548     struct stat statbuf;
549
550     /* construct a local path from a canonical (wire-format) path */
551     if ((error = ConstructLocalPath(dir, "/", expPath))) {
552         com_err(whoami, error, "Unable to construct local path");
553         return error;
554     }
555
556     /* stat the object */
557     error = stat(*expPath, &statbuf);
558     if (error) {
559         com_err(whoami, error, ";Can't stat object.");
560         return error;
561     }
562     /* now check if the object has an exportable (file/dir)  type */
563     if (!(statbuf.st_mode & S_IFDIR)) {
564         fprintf(stderr, "%s: Unacceptable object type for %s\n", whoami,
565                 *expPath);
566         return -1;
567     }
568
569     return 0;
570 }