1 /* $NetBSD: cmds.c,v 1.34 2015/08/10 07:32:49 shm Exp $ */
4 * Copyright (c) 1999-2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
34 * The Regents of the University of California. All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * Copyright (C) 1997 and 1998 WIDE Project.
63 * All rights reserved.
65 * Redistribution and use in source and binary forms, with or without
66 * modification, are permitted provided that the following conditions
68 * 1. Redistributions of source code must retain the above copyright
69 * notice, this list of conditions and the following disclaimer.
70 * 2. Redistributions in binary form must reproduce the above copyright
71 * notice, this list of conditions and the following disclaimer in the
72 * documentation and/or other materials provided with the distribution.
73 * 3. Neither the name of the project nor the names of its contributors
74 * may be used to endorse or promote products derived from this software
75 * without specific prior written permission.
77 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
78 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
81 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
91 #include <sys/cdefs.h>
93 __RCSID("$NetBSD: cmds.c,v 1.34 2015/08/10 07:32:49 shm Exp $");
96 #include <sys/param.h>
111 #include <krb5/krb5.h>
117 FE_MLSD
= 1<<0, /* if op is MLSD (MLST otherwise ) */
118 FE_ISCURDIR
= 1<<1, /* if name is the current directory */
122 const char *path
; /* full pathname */
123 const char *display
; /* name to display */
124 struct stat
*stat
; /* stat of path */
125 struct stat
*pdirstat
; /* stat of path's parent dir */
126 factflag_t flags
; /* flags */
129 static void ack(const char *);
130 static void base64_encode(const char *, size_t, char *, int);
131 static void fact_type(const char *, FILE *, factelem
*);
132 static void fact_size(const char *, FILE *, factelem
*);
133 static void fact_modify(const char *, FILE *, factelem
*);
134 static void fact_perm(const char *, FILE *, factelem
*);
135 static void fact_unique(const char *, FILE *, factelem
*);
136 static int matchgroup(gid_t
);
137 static void mlsname(FILE *, factelem
*);
138 static void replydirname(const char *, const char *);
141 const char *name
; /* name of fact */
142 int enabled
; /* if fact is enabled */
143 void (*display
)(const char *, FILE *, factelem
*);
144 /* function to display fact */
147 struct ftpfact facttab
[] = {
148 { "Type", 1, fact_type
},
150 { "Size", 1, fact_size
},
151 { "Modify", 1, fact_modify
},
152 { "Perm", 1, fact_perm
},
153 { "Unique", 1, fact_unique
},
160 #define FACTTABSIZE (sizeof(facttab) / sizeof(struct ftpfact))
162 static char cached_path
[MAXPATHLEN
+ 1] = "/";
163 static void discover_path(char *, const char *);
166 cwd(const char *path
)
170 perror_reply(550, path
);
172 show_chdir_messages(250);
174 if (getcwd(cached_path
, MAXPATHLEN
) == NULL
) {
175 discover_path(cached_path
, path
);
181 delete(const char *name
)
185 if (remove(name
) < 0) {
187 perror_reply(550, name
);
190 logxfer("delete", -1, name
, NULL
, NULL
, p
);
198 reply(-211, "Features supported");
199 cprintf(stdout
, " MDTM\r\n");
200 cprintf(stdout
, " MLST ");
201 for (i
= 0; i
< FACTTABSIZE
; i
++)
202 cprintf(stdout
, "%s%s;", facttab
[i
].name
,
203 facttab
[i
].enabled
? "*" : "");
204 cprintf(stdout
, "\r\n");
205 cprintf(stdout
, " REST STREAM\r\n");
206 cprintf(stdout
, " SIZE\r\n");
207 cprintf(stdout
, " TVFS\r\n");
212 makedir(const char *name
)
216 if (mkdir(name
, 0777) < 0) {
218 perror_reply(550, name
);
220 replydirname(name
, "directory created.");
221 logxfer("mkdir", -1, name
, NULL
, NULL
, p
);
225 mlsd(const char *path
)
228 struct stat sb
, pdirstat
;
232 char name
[MAXPATHLEN
];
235 hastypefact
= facttab
[FACT_TYPE
].enabled
;
238 if (stat(path
, &pdirstat
) == -1) {
240 perror_reply(550, path
);
243 if (! S_ISDIR(pdirstat
.st_mode
)) {
245 perror_reply(501, path
);
248 if ((dirp
= opendir(path
)) == NULL
)
251 dout
= dataconn("MLSD", (off_t
)-1, "w");
253 (void) closedir(dirp
);
257 memset(&f
, 0, sizeof(f
));
260 while ((dp
= readdir(dirp
)) != NULL
) {
261 snprintf(name
, sizeof(name
), "%s/%s", path
, dp
->d_name
);
262 if (ISDOTDIR(dp
->d_name
)) { /* special case curdir: */
265 f
.pdirstat
= NULL
; /* require stat of parent */
266 f
.display
= path
; /* set name to real name */
267 f
.flags
|= FE_ISCURDIR
; /* flag name is curdir */
269 if (ISDOTDOTDIR(dp
->d_name
)) {
274 f
.pdirstat
= &pdirstat
; /* cache parent stat */
275 f
.display
= dp
->d_name
;
276 f
.flags
&= ~FE_ISCURDIR
;
278 if (stat(name
, &sb
) == -1)
283 (void)closedir(dirp
);
285 if (ferror(dout
) != 0)
286 perror_reply(550, "Data connection");
288 reply(226, "MLSD complete.");
295 mlst(const char *path
)
302 if (stat(path
, &sb
) == -1) {
303 perror_reply(550, path
);
306 reply(-250, "MLST %s", path
);
307 memset(&f
, 0, sizeof(f
));
319 opts(const char *command
)
324 if ((ep
= strchr(command
, ' ')) != NULL
)
326 c
= lookup(cmdtab
, command
);
328 reply(502, "Unknown command '%s'.", command
);
331 if (! CMD_IMPLEMENTED(c
)) {
332 reply(502, "%s command not implemented.", c
->name
);
335 if (! CMD_HAS_OPTIONS(c
)) {
336 reply(501, "%s command does not support persistent options.",
341 /* special case: MLST */
342 if (strcasecmp(command
, "MLST") == 0) {
343 int enabled
[FACTTABSIZE
];
348 for (i
= 0; i
< sizeof(enabled
) / sizeof(int); i
++)
350 if (ep
== NULL
|| *ep
== '\0')
351 goto displaymlstopts
;
353 /* don't like spaces, and need trailing ; */
355 if (strchr(ep
, ' ') != NULL
|| ep
[len
- 1] != ';') {
357 reply(501, "Invalid MLST options");
361 while ((p
= strsep(&ep
, ";")) != NULL
) {
364 for (i
= 0; i
< FACTTABSIZE
; i
++)
365 if (strcasecmp(p
, facttab
[i
].name
) == 0) {
372 for (i
= 0; i
< FACTTABSIZE
; i
++)
373 facttab
[i
].enabled
= enabled
[i
];
374 cprintf(stdout
, "200 MLST OPTS");
375 for (i
= onedone
= 0; i
< FACTTABSIZE
; i
++) {
376 if (facttab
[i
].enabled
) {
377 cprintf(stdout
, "%s%s;", onedone
? "" : " ",
382 cprintf(stdout
, "\r\n");
388 if (ep
!= NULL
&& *ep
!= '\0')
389 REASSIGN(c
->options
, ftpd_strdup(ep
));
390 if (c
->options
!= NULL
)
391 reply(200, "Options for %s are '%s'.", c
->name
,
394 reply(200, "No options defined for %s.", c
->name
);
400 char path
[MAXPATHLEN
];
402 if (getcwd(path
, sizeof(path
) - 1) == NULL
) {
403 if (chdir(cached_path
) < 0) {
404 reply(550, "Can't get the current directory: %s.",
408 (void)strlcpy(path
, cached_path
, MAXPATHLEN
);
410 replydirname(path
, "is the current directory.");
414 removedir(const char *name
)
418 if (rmdir(name
) < 0) {
420 perror_reply(550, name
);
423 logxfer("rmdir", -1, name
, NULL
, NULL
, p
);
427 renamefrom(const char *name
)
431 if (stat(name
, &st
) < 0) {
432 perror_reply(550, name
);
435 reply(350, "File exists, ready for destination name");
436 return (ftpd_strdup(name
));
440 renamecmd(const char *from
, const char *to
)
444 if (rename(from
, to
) < 0) {
446 perror_reply(550, "rename");
449 logxfer("rename", -1, from
, to
, NULL
, p
);
453 sizecmd(const char *filename
)
460 if (stat(filename
, &stbuf
) < 0 || !S_ISREG(stbuf
.st_mode
))
461 reply(550, "%s: not a plain file.", filename
);
463 reply(213, ULLF
, (ULLT
)stbuf
.st_size
);
472 fin
= fopen(filename
, "r");
474 perror_reply(550, filename
);
477 if (fstat(fileno(fin
), &stbuf
) < 0 || !S_ISREG(stbuf
.st_mode
)) {
478 reply(550, "%s: not a plain file.", filename
);
482 if (stbuf
.st_size
> 10240) {
483 reply(550, "%s: file too large for SIZE.", filename
);
489 while((c
= getc(fin
)) != EOF
) {
490 if (c
== '\n') /* will get expanded to \r\n */
496 reply(213, LLF
, (LLT
)count
);
500 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type
]);
505 statfilecmd(const char *filename
)
510 const char *argv
[] = { INTERNAL_LS
, "-lgA", "", NULL
};
513 fin
= ftpd_popen(argv
, "r", STDOUT_FILENO
);
514 reply(-211, "status of %s:", filename
);
515 /* XXX: use fgetln() or fparseln() here? */
517 while ((c
= getc(fin
)) != EOF
) {
520 perror_reply(421, "control connection");
521 (void) ftpd_pclose(fin
);
526 perror_reply(551, filename
);
527 (void) ftpd_pclose(fin
);
532 if (atstart
&& isdigit(c
))
535 atstart
= (c
== '\n');
537 (void) ftpd_pclose(fin
);
538 reply(211, "End of Status");
547 reply(250, "%s command successful.", s
);
551 * Encode len bytes starting at clear using base64 encoding into encoded,
552 * which should be at least ((len + 2) * 4 / 3 + 1) in size.
553 * If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary
557 base64_encode(const char *clear
, size_t len
, char *encoded
, int nulterm
)
559 static const char base64
[] =
560 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
565 /* determine whether to pad with '=' or NUL terminate */
566 termchar
= nulterm
? '\0' : '=';
569 /* convert all but last 2 bytes */
570 for (i
= len
; i
> 2; i
-= 3, c
+= 3) {
571 *e
++ = base64
[(c
[0] >> 2) & 0x3f];
572 *e
++ = base64
[((c
[0] << 4) & 0x30) | ((c
[1] >> 4) & 0x0f)];
573 *e
++ = base64
[((c
[1] << 2) & 0x3c) | ((c
[2] >> 6) & 0x03)];
574 *e
++ = base64
[(c
[2]) & 0x3f];
576 /* handle slop at end */
578 *e
++ = base64
[(c
[0] >> 2) & 0x3f];
579 *e
++ = base64
[((c
[0] << 4) & 0x30) |
580 (i
> 1 ? ((c
[1] >> 4) & 0x0f) : 0)];
581 *e
++ = (i
> 1) ? base64
[(c
[1] << 2) & 0x3c] : termchar
;
588 fact_modify(const char *fact
, FILE *fd
, factelem
*fe
)
592 t
= gmtime(&(fe
->stat
->st_mtime
));
593 cprintf(fd
, "%s=%04d%02d%02d%02d%02d%02d;", fact
,
594 TM_YEAR_BASE
+ t
->tm_year
,
595 t
->tm_mon
+1, t
->tm_mday
,
596 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
600 fact_perm(const char *fact
, FILE *fd
, factelem
*fe
)
602 int rok
, wok
, xok
, pdirwok
;
605 if (fe
->stat
->st_uid
== geteuid()) {
606 rok
= ((fe
->stat
->st_mode
& S_IRUSR
) != 0);
607 wok
= ((fe
->stat
->st_mode
& S_IWUSR
) != 0);
608 xok
= ((fe
->stat
->st_mode
& S_IXUSR
) != 0);
609 } else if (matchgroup(fe
->stat
->st_gid
)) {
610 rok
= ((fe
->stat
->st_mode
& S_IRGRP
) != 0);
611 wok
= ((fe
->stat
->st_mode
& S_IWGRP
) != 0);
612 xok
= ((fe
->stat
->st_mode
& S_IXGRP
) != 0);
614 rok
= ((fe
->stat
->st_mode
& S_IROTH
) != 0);
615 wok
= ((fe
->stat
->st_mode
& S_IWOTH
) != 0);
616 xok
= ((fe
->stat
->st_mode
& S_IXOTH
) != 0);
619 cprintf(fd
, "%s=", fact
);
622 * if parent info not provided, look it up, but
623 * only if the current class has modify rights,
624 * since we only need this info in such a case.
627 if (pdir
== NULL
&& CURCLASS_FLAGS_ISSET(modify
)) {
629 char realdir
[MAXPATHLEN
], *p
;
632 len
= strlcpy(realdir
, fe
->path
, sizeof(realdir
));
633 if (len
< sizeof(realdir
) - 4) {
634 if (S_ISDIR(fe
->stat
->st_mode
))
635 strlcat(realdir
, "/..", sizeof(realdir
));
637 /* if has a /, move back to it */
638 /* otherwise use '..' */
639 if ((p
= strrchr(realdir
, '/')) != NULL
) {
644 strlcpy(realdir
, "..", sizeof(realdir
));
646 if (stat(realdir
, &dir
) == 0)
652 if (pdir
->st_uid
== geteuid())
653 pdirwok
= ((pdir
->st_mode
& S_IWUSR
) != 0);
654 else if (matchgroup(pdir
->st_gid
))
655 pdirwok
= ((pdir
->st_mode
& S_IWGRP
) != 0);
657 pdirwok
= ((pdir
->st_mode
& S_IWOTH
) != 0);
660 /* 'a': can APPE to file */
661 if (wok
&& CURCLASS_FLAGS_ISSET(upload
) && S_ISREG(fe
->stat
->st_mode
))
664 /* 'c': can create or append to files in directory */
665 if (wok
&& CURCLASS_FLAGS_ISSET(modify
) && S_ISDIR(fe
->stat
->st_mode
))
668 /* 'd': can delete file or directory */
669 if (pdirwok
&& CURCLASS_FLAGS_ISSET(modify
)) {
673 if (S_ISDIR(fe
->stat
->st_mode
)) {
677 if ((dirp
= opendir(fe
->display
)) == NULL
)
680 while ((dp
= readdir(dirp
)) != NULL
) {
681 if (ISDOTDIR(dp
->d_name
) ||
682 ISDOTDOTDIR(dp
->d_name
))
694 /* 'e': can enter directory */
695 if (xok
&& S_ISDIR(fe
->stat
->st_mode
))
698 /* 'f': can rename file or directory */
699 if (pdirwok
&& CURCLASS_FLAGS_ISSET(modify
))
702 /* 'l': can list directory */
703 if (rok
&& xok
&& S_ISDIR(fe
->stat
->st_mode
))
706 /* 'm': can create directory */
707 if (wok
&& CURCLASS_FLAGS_ISSET(modify
) && S_ISDIR(fe
->stat
->st_mode
))
710 /* 'p': can remove files in directory */
711 if (wok
&& CURCLASS_FLAGS_ISSET(modify
) && S_ISDIR(fe
->stat
->st_mode
))
714 /* 'r': can RETR file */
715 if (rok
&& S_ISREG(fe
->stat
->st_mode
))
718 /* 'w': can STOR file */
719 if (wok
&& CURCLASS_FLAGS_ISSET(upload
) && S_ISREG(fe
->stat
->st_mode
))
726 fact_size(const char *fact
, FILE *fd
, factelem
*fe
)
729 if (S_ISREG(fe
->stat
->st_mode
))
730 cprintf(fd
, "%s=" LLF
";", fact
, (LLT
)fe
->stat
->st_size
);
734 fact_type(const char *fact
, FILE *fd
, factelem
*fe
)
737 cprintf(fd
, "%s=", fact
);
738 switch (fe
->stat
->st_mode
& S_IFMT
) {
740 if (fe
->flags
& FE_MLSD
) {
741 if ((fe
->flags
& FE_ISCURDIR
) || ISDOTDIR(fe
->display
))
743 else if (ISDOTDOTDIR(fe
->display
))
755 cprintf(fd
, "OS.unix=fifo");
757 case S_IFLNK
: /* XXX: probably a NO-OP with stat() */
758 cprintf(fd
, "OS.unix=slink");
761 cprintf(fd
, "OS.unix=socket");
765 cprintf(fd
, "OS.unix=%s-" ULLF
"/" ULLF
,
766 S_ISBLK(fe
->stat
->st_mode
) ? "blk" : "chr",
767 (ULLT
)major(fe
->stat
->st_rdev
),
768 (ULLT
)minor(fe
->stat
->st_rdev
));
771 cprintf(fd
, "OS.unix=UNKNOWN(0%o)", fe
->stat
->st_mode
& S_IFMT
);
778 fact_unique(const char *fact
, FILE *fd
, factelem
*fe
)
780 char obuf
[(sizeof(dev_t
) + sizeof(ino_t
) + 2) * 4 / 3 + 2];
781 char tbuf
[sizeof(dev_t
) + sizeof(ino_t
)];
784 (char *)&(fe
->stat
->st_dev
), sizeof(dev_t
));
785 memcpy(tbuf
+ sizeof(dev_t
),
786 (char *)&(fe
->stat
->st_ino
), sizeof(ino_t
));
787 base64_encode(tbuf
, sizeof(dev_t
) + sizeof(ino_t
), obuf
, 1);
788 cprintf(fd
, "%s=%s;", fact
, obuf
);
792 matchgroup(gid_t gid
)
796 for (i
= 0; i
< gidcount
; i
++)
797 if (gid
== gidlist
[i
])
803 mlsname(FILE *fp
, factelem
*fe
)
805 char realfile
[MAXPATHLEN
];
809 for (i
= 0; i
< FACTTABSIZE
; i
++) {
810 if (facttab
[i
].enabled
)
811 (facttab
[i
].display
)(facttab
[i
].name
, fp
, fe
);
813 if ((fe
->flags
& FE_MLSD
) &&
814 !(fe
->flags
& FE_ISCURDIR
) && !ISDOTDIR(fe
->display
)) {
815 /* if MLSD and not "." entry, display as-is */
818 /* if MLST, or MLSD and "." entry, realpath(3) it */
819 if (realpath(fe
->display
, realfile
) != NULL
)
822 cprintf(fp
, " %s\r\n", userf
? realfile
: fe
->display
);
826 replydirname(const char *name
, const char *message
)
829 char npath
[MAXPATHLEN
* 2];
832 ep
= &npath
[sizeof(npath
) - 1];
846 reply(257, "\"%s\" %s", npath
, message
);
850 discover_path(char *last_path
, const char *new_path
)
852 char tp
[MAXPATHLEN
+ 1] = "";
853 char tq
[MAXPATHLEN
+ 1] = "";
858 struct stat st1
, st2
;
860 if (new_path
[0] != '/') {
861 (void)strlcpy(tp
, last_path
, MAXPATHLEN
);
862 (void)strlcat(tp
, "/", MAXPATHLEN
);
864 (void)strlcat(tp
, new_path
, MAXPATHLEN
);
865 (void)strlcat(tp
, "/", MAXPATHLEN
);
868 * resolve symlinks. A symlink may introduce another symlink, so we
869 * loop trying to resolve symlinks until we don't find any of them.
872 /* Collapse any // into / */
873 while ((cp
= strstr(tp
, "//")) != NULL
)
874 (void)memmove(cp
, cp
+ 1, strlen(cp
) - 1 + 1);
876 /* Collapse any /./ into / */
877 while ((cp
= strstr(tp
, "/./")) != NULL
)
878 (void)memmove(cp
, cp
+ 2, strlen(cp
) - 2 + 1);
883 while ((cp
= strstr(cp
+ 1, "/")) != NULL
) {
884 sz1
= (unsigned long)cp
- (unsigned long)tp
;
885 if (sz1
> MAXPATHLEN
)
888 sz2
= readlink(tp
, tq
, MAXPATHLEN
);
891 /* If this is not a symlink, move to next / */
896 * We found a symlink, so we will have to
897 * do one more pass to check there is no
898 * more symlink in the path
903 * Null terminate the string and remove trailing /
907 if (tq
[sz2
- 1] == '/')
911 * Is this an absolute link or a relative link?
915 if (strlen(cp
) + sz2
> MAXPATHLEN
)
917 memmove(tp
+ sz2
, cp
, strlen(cp
) + 1);
921 for (cq
= cp
- 1; *cq
!= '/'; cq
--);
923 ((unsigned long)cq
- (unsigned long)cp
)
924 + 1 + sz2
> MAXPATHLEN
)
926 (void)memmove(cq
+ 1 + sz2
,
928 (void)memcpy(cq
+ 1, tq
, sz2
);
932 * start over, looking for new symlinks
936 } while (nomorelink
== 0);
938 /* Collapse any /foo/../ into /foo/ */
939 while ((cp
= strstr(tp
, "/../")) != NULL
) {
940 /* ^/../foo/ becomes ^/foo/ */
942 (void)memmove(cp
, cp
+ 3,
945 for (cq
= cp
- 1; *cq
!= '/'; cq
--);
946 (void)memmove(cq
, cp
+ 3,
951 /* strip strailing / */
953 tp
[strlen(tp
) - 1] = '\0';
955 /* check that the path is correct */
956 if (stat(tp
, &st1
) == -1 || stat(".", &st2
) == -1)
958 if ((st1
.st_dev
!= st2
.st_dev
) || (st1
.st_ino
!= st2
.st_ino
))
961 (void)strlcpy(last_path
, tp
, MAXPATHLEN
);
965 (void)strlcat(last_path
, "/", MAXPATHLEN
);
966 (void)strlcat(last_path
, new_path
, MAXPATHLEN
);