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