cmds.c revision 1.2 1 /* $NetBSD: cmds.c,v 1.2 2000/06/16 23:17:41 explorer Exp $ */
2
3 /*
4 * Copyright (c) 1999-2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
41 * The Regents of the University of California. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
70 */
71
72 /*
73 * Copyright (C) 1997 and 1998 WIDE Project.
74 * All rights reserved.
75 *
76 * Redistribution and use in source and binary forms, with or without
77 * modification, are permitted provided that the following conditions
78 * are met:
79 * 1. Redistributions of source code must retain the above copyright
80 * notice, this list of conditions and the following disclaimer.
81 * 2. Redistributions in binary form must reproduce the above copyright
82 * notice, this list of conditions and the following disclaimer in the
83 * documentation and/or other materials provided with the distribution.
84 * 3. Neither the name of the project nor the names of its contributors
85 * may be used to endorse or promote products derived from this software
86 * without specific prior written permission.
87 *
88 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
89 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
90 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
91 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
92 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
93 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
94 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
95 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
96 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
97 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
98 * SUCH DAMAGE.
99 */
100
101
102 #include <sys/cdefs.h>
103 #ifndef lint
104 __RCSID("$NetBSD: cmds.c,v 1.2 2000/06/16 23:17:41 explorer Exp $");
105 #endif /* not lint */
106
107 #include <sys/param.h>
108 #include <sys/stat.h>
109
110 #include <arpa/ftp.h>
111
112 #include <dirent.h>
113 #include <errno.h>
114 #include <setjmp.h>
115 #include <stdio.h>
116 #include <stdlib.h>
117 #include <string.h>
118 #include <tzfile.h>
119 #include <unistd.h>
120
121 #ifdef KERBEROS5
122 #include <krb5/krb5.h>
123 #endif
124
125 #include "extern.h"
126
127 typedef struct {
128 const char *path; /* full pathname */
129 const char *display; /* name to display */
130 struct stat *stat; /* stat of path */
131 struct stat *pdirstat; /* stat of path's parent dir */
132 int iscurdir; /* nonzero if name is the current dir */
133 } factelem;
134
135 static void ack(const char *);
136 static void fact_type(const char *, FILE *, factelem *);
137 static void fact_size(const char *, FILE *, factelem *);
138 static void fact_modify(const char *, FILE *, factelem *);
139 static void fact_perm(const char *, FILE *, factelem *);
140 static void fact_unique(const char *, FILE *, factelem *);
141 static void fact_unix_group(const char *, FILE *, factelem *);
142 static void fact_unix_mode(const char *, FILE *, factelem *);
143 static void fact_unix_owner(const char *, FILE *, factelem *);
144 static int matchgroup(gid_t, gid_t *, int);
145 static void mlsname(FILE *, factelem *);
146 static void replydirname(const char *, const char *);
147
148 struct ftpfact {
149 const char *name; /* name of fact */
150 int enabled; /* if fact is enabled */
151 void (*display)(const char *, FILE *, factelem *);
152 /* function to display fact */
153 };
154
155 struct ftpfact facttab[] = {
156 { "Type", 1, fact_type },
157 { "Size", 1, fact_size },
158 { "Modify", 1, fact_modify },
159 { "Perm", 1, fact_perm },
160 { "Unique", 1, fact_unique },
161 { "UNIX.mode", 1, fact_unix_mode },
162 { "UNIX.owner", 0, fact_unix_owner },
163 { "UNIX.group", 0, fact_unix_group },
164 /* "Create" */
165 /* "Lang" */
166 /* "Media-Type" */
167 /* "CharSet" */
168 { NULL, NULL, },
169 };
170
171
172 void
173 cwd(const char *path)
174 {
175
176 if (chdir(path) < 0)
177 perror_reply(550, path);
178 else {
179 show_chdir_messages(250);
180 ack("CWD");
181 }
182 }
183
184 void
185 delete(const char *name)
186 {
187 char *p = NULL;
188
189 if (remove(name) < 0) {
190 p = strerror(errno);
191 perror_reply(550, name);
192 } else
193 ack("DELE");
194 logcmd("delete", -1, name, NULL, NULL, p);
195 }
196
197 void
198 feat(void)
199 {
200 int i;
201
202 lreply(211, "Features supported");
203 lreply(-1, " MDTM");
204 lreply(-2, " MLST ");
205 for (i = 0; facttab[i].name; i++)
206 lreply(-2, "%s%s;", facttab[i].name,
207 facttab[i].enabled ? "*" : "");
208 lreply(-1, "");
209 lreply(-1, " REST STREAM");
210 lreply(-1, " SIZE");
211 lreply(-1, " TVFS");
212 reply(211, "End");
213 }
214
215 void
216 makedir(const char *name)
217 {
218 char *p = NULL;
219
220 if (mkdir(name, 0777) < 0) {
221 p = strerror(errno);
222 perror_reply(550, name);
223 } else
224 replydirname(name, "directory created.");
225 logcmd("mkdir", -1, name, NULL, NULL, p);
226 }
227
228 void
229 mlsd(const char *path)
230 {
231 FILE *dout;
232 DIR *dirp;
233 struct dirent *dp;
234 struct stat sb, pdirstat;
235 char name[MAXPATHLEN];
236 factelem f;
237
238 if (path == NULL)
239 path = ".";
240 if (stat(path, &pdirstat) == -1) {
241 mlsdperror:
242 perror_reply(550, path);
243 return;
244 }
245 if (! S_ISDIR(pdirstat.st_mode)) {
246 errno = ENOTDIR;
247 goto mlsdperror;
248 }
249 #ifdef DEBUG /* send mlsd to ctrl connection not data connection */
250 dout = stdout;
251 lreply(250, "MLSD %s", path);
252 #else
253 dout = dataconn("MLSD", (off_t)-1, "w");
254 if (dout == NULL)
255 return;
256 #endif
257
258 if ((dirp = opendir(path)) == NULL)
259 goto mlsdperror;
260 f.stat = &sb;
261 while ((dp = readdir(dirp)) != NULL) {
262 snprintf(name, sizeof(name), "%s/%s", path, dp->d_name);
263 /* printf("got >%s< >%s<\n", dp->d_name, name); */
264 if (stat(name, &sb) == -1)
265 continue;
266 f.path = name;
267 if (ISDOTDIR(dp->d_name)) { /* special case curdir: */
268 f.display = path; /* set name to real name */
269 f.iscurdir = 1; /* flag name is curdir */
270 f.pdirstat = NULL; /* require stat of parent */
271 } else {
272 f.display = dp->d_name;
273 f.iscurdir = 0;
274 if (ISDOTDOTDIR(dp->d_name))
275 f.pdirstat = NULL;
276 else
277 f.pdirstat = &pdirstat; /* cache parent stat */
278 }
279 mlsname(dout, &f);
280 }
281 (void)closedir(dirp);
282
283 #ifdef DEBUG
284 reply(250, "End");
285 #else
286 if (ferror(dout) != 0)
287 perror_reply(550, "Data connection");
288 else
289 reply(226, "MLSD complete.");
290 (void) fclose(dout);
291 total_xfers_out++;
292 total_xfers++;
293 #endif
294 }
295
296 void
297 mlst(const char *path)
298 {
299 struct stat sb;
300 factelem f;
301
302 if (path == NULL)
303 path = ".";
304 if (stat(path, &sb) == -1) {
305 perror_reply(550, path);
306 return;
307 }
308 lreply(250, "MLST %s", path);
309 f.path = path;
310 f.display = path;
311 f.stat = &sb;
312 f.pdirstat = NULL;
313 f.iscurdir = 0;
314 mlsname(stdout, &f);
315 reply(250, "End");
316 }
317
318
319 void
320 opts(const char *command)
321 {
322 struct tab *c;
323 char *ep;
324
325 if ((ep = strchr(command, ' ')) != NULL)
326 *ep++ = '\0';
327 c = lookup(cmdtab, command);
328 if (c == NULL) {
329 reply(502, "Unknown command %s.", command);
330 return;
331 }
332 if (! CMD_IMPLEMENTED(c)) {
333 reply(501, "%s command not implemented.", c->name);
334 return;
335 }
336 if (! CMD_HAS_OPTIONS(c)) {
337 reply(501, "%s command does not support persistent options.",
338 c->name);
339 return;
340 }
341
342 /* special case: MLST */
343 if (strcasecmp(command, "MLST") == 0) {
344 int i, onedone;
345 char *p;
346
347 for (i = 0; facttab[i].name; i++)
348 facttab[i].enabled = 0;
349 if (ep == NULL || *ep == '\0')
350 goto displaymlstopts;
351
352 /* don't like spaces, and need trailing ; */
353 if (strchr(ep, ' ') != NULL || ep[strlen(ep) - 1] != ';') {
354 reply(501, "Invalid MLST options");
355 return;
356 }
357 while ((p = strsep(&ep, ";")) != NULL) {
358 for (i = 0; facttab[i].name; i++)
359 if (strcasecmp(p, facttab[i].name) == 0) {
360 facttab[i].enabled = 1;
361 break;
362 }
363 }
364
365 displaymlstopts:
366 lreply(-2, "200 MLST OPTS");
367 for (i = onedone = 0; facttab[i].name; i++) {
368 if (facttab[i].enabled) {
369 lreply(-2, "%s%s;", onedone ? "" : " ",
370 facttab[i].name);
371 onedone++;
372 }
373 }
374 lreply(-1, "");
375 return;
376 }
377
378 /* default cases */
379 if (ep != NULL && *ep != '\0')
380 REASSIGN(c->options, xstrdup(ep));
381 if (c->options != NULL)
382 reply(200, "Options for %s are '%s'.", c->name,
383 c->options);
384 else
385 reply(200, "No options defined for %s.", c->name);
386 }
387
388 void
389 pwd(void)
390 {
391 char path[MAXPATHLEN];
392
393 if (getcwd(path, sizeof(path) - 1) == NULL)
394 reply(550, "Can't get the current directory: %s.",
395 strerror(errno));
396 else
397 replydirname(path, "is the current directory.");
398 }
399
400 void
401 removedir(const char *name)
402 {
403 char *p = NULL;
404
405 if (rmdir(name) < 0) {
406 p = strerror(errno);
407 perror_reply(550, name);
408 } else
409 ack("RMD");
410 logcmd("rmdir", -1, name, NULL, NULL, p);
411 }
412
413 char *
414 renamefrom(const char *name)
415 {
416 struct stat st;
417
418 if (stat(name, &st) < 0) {
419 perror_reply(550, name);
420 return (NULL);
421 }
422 reply(350, "File exists, ready for destination name");
423 return (xstrdup(name));
424 }
425
426 void
427 renamecmd(const char *from, const char *to)
428 {
429 char *p = NULL;
430
431 if (rename(from, to) < 0) {
432 p = strerror(errno);
433 perror_reply(550, "rename");
434 } else
435 ack("RNTO");
436 logcmd("rename", -1, from, to, NULL, p);
437 }
438
439 void
440 sizecmd(const char *filename)
441 {
442 switch (type) {
443 case TYPE_L:
444 case TYPE_I: {
445 struct stat stbuf;
446 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
447 reply(550, "%s: not a plain file.", filename);
448 else
449 reply(213, "%qu", (qufmt_t)stbuf.st_size);
450 break; }
451 case TYPE_A: {
452 FILE *fin;
453 int c;
454 off_t count;
455 struct stat stbuf;
456 fin = fopen(filename, "r");
457 if (fin == NULL) {
458 perror_reply(550, filename);
459 return;
460 }
461 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
462 reply(550, "%s: not a plain file.", filename);
463 (void) fclose(fin);
464 return;
465 }
466
467 count = 0;
468 while((c=getc(fin)) != EOF) {
469 if (c == '\n') /* will get expanded to \r\n */
470 count++;
471 count++;
472 }
473 (void) fclose(fin);
474
475 reply(213, "%qd", (qdfmt_t)count);
476 break; }
477 default:
478 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
479 }
480 }
481
482 void
483 statfilecmd(const char *filename)
484 {
485 FILE *fin;
486 int c;
487 char *argv[] = { INTERNAL_LS, "-lgA", "", NULL };
488
489 argv[2] = (char *)filename;
490 fin = ftpd_popen(argv, "r", STDOUT_FILENO);
491 lreply(211, "status of %s:", filename);
492 while ((c = getc(fin)) != EOF) {
493 if (c == '\n') {
494 if (ferror(stdout)){
495 perror_reply(421, "control connection");
496 (void) ftpd_pclose(fin);
497 dologout(1);
498 /* NOTREACHED */
499 }
500 if (ferror(fin)) {
501 perror_reply(551, filename);
502 (void) ftpd_pclose(fin);
503 return;
504 }
505 (void) putchar('\r');
506 total_bytes++;
507 total_bytes_out++;
508 }
509 (void) putchar(c);
510 total_bytes++;
511 total_bytes_out++;
512 }
513 (void) ftpd_pclose(fin);
514 reply(211, "End of Status");
515 }
516
517 /* -- */
518
519 static void
520 ack(const char *s)
521 {
522
523 reply(250, "%s command successful.", s);
524 }
525
526 static void
527 fact_modify(const char *fact, FILE *fd, factelem *fe)
528 {
529 struct tm *t;
530
531 t = gmtime(&(fe->stat->st_mtime));
532 fprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact,
533 TM_YEAR_BASE + t->tm_year,
534 t->tm_mon+1, t->tm_mday,
535 t->tm_hour, t->tm_min, t->tm_sec);
536 }
537
538 static void
539 fact_perm(const char *fact, FILE *fd, factelem *fe)
540 {
541 int rok, wok, xok, pdirwok, ngid;
542 struct stat *pdir;
543 gid_t gids[NGROUPS_MAX];
544
545 ngid = getgroups(sizeof(gids), gids);
546 if (fe->stat->st_uid == geteuid()) {
547 rok = ((fe->stat->st_mode & S_IRUSR) != 0);
548 wok = ((fe->stat->st_mode & S_IWUSR) != 0);
549 xok = ((fe->stat->st_mode & S_IXUSR) != 0);
550 } else if (matchgroup(fe->stat->st_gid, gids, ngid)) {
551 rok = ((fe->stat->st_mode & S_IRGRP) != 0);
552 wok = ((fe->stat->st_mode & S_IWGRP) != 0);
553 xok = ((fe->stat->st_mode & S_IXGRP) != 0);
554 } else {
555 rok = ((fe->stat->st_mode & S_IROTH) != 0);
556 wok = ((fe->stat->st_mode & S_IWOTH) != 0);
557 xok = ((fe->stat->st_mode & S_IXOTH) != 0);
558 }
559
560 fprintf(fd, "%s=", fact);
561
562 /*
563 * if parent info not provided, look it up, but
564 * only if the current class has modify rights,
565 * since we only need this info in such a case.
566 */
567 pdir = fe->pdirstat;
568 if (pdir == NULL && curclass.modify) {
569 size_t len;
570 char realdir[MAXPATHLEN], *p;
571 struct stat dir;
572
573 len = strlcpy(realdir, fe->path, sizeof(realdir));
574 /* printf("[path=%s]", fe->path); */
575 if (len < sizeof(realdir) - 4) {
576 if (S_ISDIR(fe->stat->st_mode))
577 strlcat(realdir, "/..", sizeof(realdir));
578 else {
579 /* if has a /, move back to it */
580 /* otherwise use '..' */
581 if ((p = strrchr(realdir, '/')) != NULL) {
582 if (p == realdir)
583 p++;
584 *p = '\0';
585 } else
586 strlcpy(realdir, "..", sizeof(realdir));
587 }
588 /* printf("[real=%s]", realdir); */
589 if (stat(realdir, &dir) == 0)
590 pdir = &dir;
591 }
592 }
593 pdirwok = 0;
594 if (pdir != NULL) {
595 if (pdir->st_uid == geteuid())
596 pdirwok = ((pdir->st_mode & S_IWUSR) != 0);
597 else if (matchgroup(pdir->st_gid, gids, ngid))
598 pdirwok = ((pdir->st_mode & S_IWGRP) != 0);
599 else
600 pdirwok = ((pdir->st_mode & S_IWOTH) != 0);
601 }
602
603 /* printf("[euid=%d,r%d,w%d,x%d,pw%d]", geteuid(), rok, wok, xok, pdirwok);
604 */
605
606 /* 'a': can APPE to file */
607 if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
608 fputs("a", fd);
609
610 /* 'c': can create or append to files in directory */
611 if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
612 fputs("c", fd);
613
614 /* 'd': can delete file or directory */
615 if (pdirwok && curclass.modify) {
616 int candel;
617
618 candel = 1;
619 if (S_ISDIR(fe->stat->st_mode)) {
620 DIR *dirp;
621 struct dirent *dp;
622
623 if ((dirp = opendir(fe->display)) == NULL)
624 candel = 0;
625 else {
626 while ((dp = readdir(dirp)) != NULL) {
627 if (ISDOTDIR(dp->d_name) ||
628 ISDOTDOTDIR(dp->d_name))
629 continue;
630 candel = 0;
631 break;
632 }
633 closedir(dirp);
634 }
635 }
636 if (candel)
637 fputs("d", fd);
638 }
639
640 /* 'e': can enter directory */
641 if (xok && S_ISDIR(fe->stat->st_mode))
642 fputs("e", fd);
643
644 /* 'f': can rename file or directory */
645 if (pdirwok && curclass.modify)
646 fputs("f", fd);
647
648 /* 'l': can list directory */
649 if (rok && xok && S_ISDIR(fe->stat->st_mode))
650 fputs("l", fd);
651
652 /* 'm': can create directory */
653 if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
654 fputs("m", fd);
655
656 /* 'p': can remove files in directory */
657 if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
658 fputs("p", fd);
659
660 /* 'r': can RETR file */
661 if (rok && S_ISREG(fe->stat->st_mode))
662 fputs("r", fd);
663
664 /* 'w': can STOR file */
665 if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
666 fputs("w", fd);
667
668 fputc(';', fd);
669 }
670
671 static void
672 fact_size(const char *fact, FILE *fd, factelem *fe)
673 {
674
675 if (S_ISREG(fe->stat->st_mode))
676 fprintf(fd, "%s=%lld;", fact, (long long)fe->stat->st_size);
677 }
678
679 static void
680 fact_type(const char *fact, FILE *fd, factelem *fe)
681 {
682
683 fprintf(fd, "%s=", fact);
684 switch (fe->stat->st_mode & S_IFMT) {
685 case S_IFDIR:
686 if (fe->iscurdir || ISDOTDIR(fe->display))
687 fputs("cdir", fd);
688 else if (ISDOTDOTDIR(fe->display))
689 fputs("pdir", fd);
690 else
691 fputs("dir", fd);
692 break;
693 case S_IFREG:
694 fputs("file", fd);
695 break;
696 case S_IFIFO:
697 fputs("OS.unix=fifo", fd);
698 break;
699 case S_IFLNK:
700 fputs("OS.unix=slink", fd);
701 break;
702 case S_IFSOCK:
703 fputs("OS.unix=socket", fd);
704 break;
705 case S_IFBLK:
706 case S_IFCHR:
707 fprintf(fd, "OS.unix=%s-%d/%d",
708 S_ISBLK(fe->stat->st_mode) ? "blk" : "chr",
709 major(fe->stat->st_rdev), minor(fe->stat->st_rdev));
710 break;
711 default:
712 fprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT);
713 break;
714 }
715 fputc(';', fd);
716 }
717
718 static void
719 fact_unique(const char *fact, FILE *fd, factelem *fe)
720 {
721
722 fprintf(fd, "%s=%04x%08x;", fact, fe->stat->st_dev, fe->stat->st_ino);
723 }
724
725 static void
726 fact_unix_mode(const char *fact, FILE *fd, factelem *fe)
727 {
728
729 fprintf(fd, "%s=%03o;", fact, fe->stat->st_mode & ACCESSPERMS);
730 }
731
732 static void
733 fact_unix_owner(const char *fact, FILE *fd, factelem *fe)
734 {
735
736 fprintf(fd, "%s=%d;", fact, (int)fe->stat->st_uid);
737 }
738
739 static void
740 fact_unix_group(const char *fact, FILE *fd, factelem *fe)
741 {
742
743 fprintf(fd, "%s=%d;", fact, (int)fe->stat->st_gid);
744 }
745
746 static int
747 matchgroup(gid_t gid, gid_t *gidlist, int ngids)
748 {
749 int i;
750
751 for (i = 0; i < ngids; i++)
752 if (gid == gidlist[i])
753 return(1);
754 return (0);
755 }
756
757 static void
758 mlsname(FILE *fp, factelem *fe)
759 {
760 int i;
761
762 fputs(" ", fp);
763 for (i = 0; facttab[i].name; i++) {
764 if (facttab[i].enabled)
765 (facttab[i].display)(facttab[i].name, fp, fe);
766 }
767 fprintf(fp, " %s\r\n", fe->display);
768 }
769
770 static void
771 replydirname(const char *name, const char *message)
772 {
773 char npath[MAXPATHLEN];
774 int i;
775
776 for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
777 npath[i] = *name;
778 if (*name == '"')
779 npath[++i] = '"';
780 }
781 npath[i] = '\0';
782 reply(257, "\"%s\" %s", npath, message);
783 }
784