1 1.35 christos /* $NetBSD: cmds.c,v 1.35 2016/01/17 14:46:07 christos Exp $ */ 2 1.1 lukem 3 1.1 lukem /* 4 1.29 lukem * Copyright (c) 1999-2009 The NetBSD Foundation, Inc. 5 1.1 lukem * All rights reserved. 6 1.1 lukem * 7 1.1 lukem * This code is derived from software contributed to The NetBSD Foundation 8 1.1 lukem * by Luke Mewburn. 9 1.1 lukem * 10 1.1 lukem * Redistribution and use in source and binary forms, with or without 11 1.1 lukem * modification, are permitted provided that the following conditions 12 1.1 lukem * are met: 13 1.1 lukem * 1. Redistributions of source code must retain the above copyright 14 1.1 lukem * notice, this list of conditions and the following disclaimer. 15 1.1 lukem * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 lukem * notice, this list of conditions and the following disclaimer in the 17 1.1 lukem * documentation and/or other materials provided with the distribution. 18 1.1 lukem * 19 1.1 lukem * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 lukem * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 lukem * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 lukem * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 lukem * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 lukem * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 lukem * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 lukem * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 lukem * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 lukem * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 lukem * POSSIBILITY OF SUCH DAMAGE. 30 1.1 lukem */ 31 1.1 lukem 32 1.1 lukem /* 33 1.1 lukem * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 34 1.1 lukem * The Regents of the University of California. All rights reserved. 35 1.1 lukem * 36 1.1 lukem * Redistribution and use in source and binary forms, with or without 37 1.1 lukem * modification, are permitted provided that the following conditions 38 1.1 lukem * are met: 39 1.1 lukem * 1. Redistributions of source code must retain the above copyright 40 1.1 lukem * notice, this list of conditions and the following disclaimer. 41 1.1 lukem * 2. Redistributions in binary form must reproduce the above copyright 42 1.1 lukem * notice, this list of conditions and the following disclaimer in the 43 1.1 lukem * documentation and/or other materials provided with the distribution. 44 1.22 agc * 3. Neither the name of the University nor the names of its contributors 45 1.1 lukem * may be used to endorse or promote products derived from this software 46 1.1 lukem * without specific prior written permission. 47 1.1 lukem * 48 1.1 lukem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 1.1 lukem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 1.1 lukem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 1.1 lukem * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 1.1 lukem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 1.1 lukem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 1.1 lukem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 1.1 lukem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 1.1 lukem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 1.1 lukem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 1.1 lukem * SUCH DAMAGE. 59 1.1 lukem */ 60 1.1 lukem 61 1.1 lukem /* 62 1.1 lukem * Copyright (C) 1997 and 1998 WIDE Project. 63 1.1 lukem * All rights reserved. 64 1.1 lukem * 65 1.1 lukem * Redistribution and use in source and binary forms, with or without 66 1.1 lukem * modification, are permitted provided that the following conditions 67 1.1 lukem * are met: 68 1.1 lukem * 1. Redistributions of source code must retain the above copyright 69 1.1 lukem * notice, this list of conditions and the following disclaimer. 70 1.1 lukem * 2. Redistributions in binary form must reproduce the above copyright 71 1.1 lukem * notice, this list of conditions and the following disclaimer in the 72 1.1 lukem * documentation and/or other materials provided with the distribution. 73 1.1 lukem * 3. Neither the name of the project nor the names of its contributors 74 1.1 lukem * may be used to endorse or promote products derived from this software 75 1.1 lukem * without specific prior written permission. 76 1.1 lukem * 77 1.1 lukem * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 78 1.1 lukem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 79 1.1 lukem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 80 1.1 lukem * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 81 1.1 lukem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 82 1.1 lukem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 83 1.1 lukem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 84 1.1 lukem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 85 1.1 lukem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 86 1.1 lukem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 87 1.1 lukem * SUCH DAMAGE. 88 1.1 lukem */ 89 1.1 lukem 90 1.1 lukem 91 1.1 lukem #include <sys/cdefs.h> 92 1.1 lukem #ifndef lint 93 1.35 christos __RCSID("$NetBSD: cmds.c,v 1.35 2016/01/17 14:46:07 christos Exp $"); 94 1.1 lukem #endif /* not lint */ 95 1.1 lukem 96 1.1 lukem #include <sys/param.h> 97 1.1 lukem #include <sys/stat.h> 98 1.1 lukem 99 1.1 lukem #include <arpa/ftp.h> 100 1.1 lukem 101 1.1 lukem #include <dirent.h> 102 1.1 lukem #include <errno.h> 103 1.1 lukem #include <stdio.h> 104 1.1 lukem #include <stdlib.h> 105 1.1 lukem #include <string.h> 106 1.1 lukem #include <tzfile.h> 107 1.1 lukem #include <unistd.h> 108 1.19 itojun #include <ctype.h> 109 1.2 explorer 110 1.2 explorer #ifdef KERBEROS5 111 1.2 explorer #include <krb5/krb5.h> 112 1.2 explorer #endif 113 1.1 lukem 114 1.1 lukem #include "extern.h" 115 1.1 lukem 116 1.16 lukem typedef enum { 117 1.16 lukem FE_MLSD = 1<<0, /* if op is MLSD (MLST otherwise ) */ 118 1.16 lukem FE_ISCURDIR = 1<<1, /* if name is the current directory */ 119 1.16 lukem } factflag_t; 120 1.16 lukem 121 1.1 lukem typedef struct { 122 1.1 lukem const char *path; /* full pathname */ 123 1.1 lukem const char *display; /* name to display */ 124 1.1 lukem struct stat *stat; /* stat of path */ 125 1.1 lukem struct stat *pdirstat; /* stat of path's parent dir */ 126 1.16 lukem factflag_t flags; /* flags */ 127 1.1 lukem } factelem; 128 1.1 lukem 129 1.1 lukem static void ack(const char *); 130 1.3 lukem static void base64_encode(const char *, size_t, char *, int); 131 1.1 lukem static void fact_type(const char *, FILE *, factelem *); 132 1.1 lukem static void fact_size(const char *, FILE *, factelem *); 133 1.1 lukem static void fact_modify(const char *, FILE *, factelem *); 134 1.1 lukem static void fact_perm(const char *, FILE *, factelem *); 135 1.1 lukem static void fact_unique(const char *, FILE *, factelem *); 136 1.3 lukem static int matchgroup(gid_t); 137 1.1 lukem static void mlsname(FILE *, factelem *); 138 1.1 lukem static void replydirname(const char *, const char *); 139 1.1 lukem 140 1.1 lukem struct ftpfact { 141 1.1 lukem const char *name; /* name of fact */ 142 1.1 lukem int enabled; /* if fact is enabled */ 143 1.1 lukem void (*display)(const char *, FILE *, factelem *); 144 1.1 lukem /* function to display fact */ 145 1.1 lukem }; 146 1.1 lukem 147 1.1 lukem struct ftpfact facttab[] = { 148 1.1 lukem { "Type", 1, fact_type }, 149 1.3 lukem #define FACT_TYPE 0 150 1.1 lukem { "Size", 1, fact_size }, 151 1.1 lukem { "Modify", 1, fact_modify }, 152 1.1 lukem { "Perm", 1, fact_perm }, 153 1.1 lukem { "Unique", 1, fact_unique }, 154 1.1 lukem /* "Create" */ 155 1.1 lukem /* "Lang" */ 156 1.1 lukem /* "Media-Type" */ 157 1.1 lukem /* "CharSet" */ 158 1.1 lukem }; 159 1.1 lukem 160 1.4 lukem #define FACTTABSIZE (sizeof(facttab) / sizeof(struct ftpfact)) 161 1.4 lukem 162 1.20 manu static char cached_path[MAXPATHLEN + 1] = "/"; 163 1.20 manu static void discover_path(char *, const char *); 164 1.1 lukem 165 1.1 lukem void 166 1.1 lukem cwd(const char *path) 167 1.1 lukem { 168 1.1 lukem 169 1.1 lukem if (chdir(path) < 0) 170 1.1 lukem perror_reply(550, path); 171 1.1 lukem else { 172 1.1 lukem show_chdir_messages(250); 173 1.1 lukem ack("CWD"); 174 1.20 manu if (getcwd(cached_path, MAXPATHLEN) == NULL) { 175 1.20 manu discover_path(cached_path, path); 176 1.20 manu } 177 1.1 lukem } 178 1.1 lukem } 179 1.1 lukem 180 1.1 lukem void 181 1.1 lukem delete(const char *name) 182 1.1 lukem { 183 1.1 lukem char *p = NULL; 184 1.1 lukem 185 1.1 lukem if (remove(name) < 0) { 186 1.1 lukem p = strerror(errno); 187 1.1 lukem perror_reply(550, name); 188 1.1 lukem } else 189 1.1 lukem ack("DELE"); 190 1.10 lukem logxfer("delete", -1, name, NULL, NULL, p); 191 1.1 lukem } 192 1.1 lukem 193 1.1 lukem void 194 1.1 lukem feat(void) 195 1.1 lukem { 196 1.30 lukem size_t i; 197 1.1 lukem 198 1.3 lukem reply(-211, "Features supported"); 199 1.3 lukem cprintf(stdout, " MDTM\r\n"); 200 1.3 lukem cprintf(stdout, " MLST "); 201 1.4 lukem for (i = 0; i < FACTTABSIZE; i++) 202 1.3 lukem cprintf(stdout, "%s%s;", facttab[i].name, 203 1.1 lukem facttab[i].enabled ? "*" : ""); 204 1.3 lukem cprintf(stdout, "\r\n"); 205 1.3 lukem cprintf(stdout, " REST STREAM\r\n"); 206 1.3 lukem cprintf(stdout, " SIZE\r\n"); 207 1.3 lukem cprintf(stdout, " TVFS\r\n"); 208 1.1 lukem reply(211, "End"); 209 1.1 lukem } 210 1.1 lukem 211 1.1 lukem void 212 1.1 lukem makedir(const char *name) 213 1.1 lukem { 214 1.1 lukem char *p = NULL; 215 1.1 lukem 216 1.1 lukem if (mkdir(name, 0777) < 0) { 217 1.1 lukem p = strerror(errno); 218 1.1 lukem perror_reply(550, name); 219 1.1 lukem } else 220 1.1 lukem replydirname(name, "directory created."); 221 1.10 lukem logxfer("mkdir", -1, name, NULL, NULL, p); 222 1.1 lukem } 223 1.1 lukem 224 1.1 lukem void 225 1.1 lukem mlsd(const char *path) 226 1.1 lukem { 227 1.3 lukem struct dirent *dp; 228 1.3 lukem struct stat sb, pdirstat; 229 1.1 lukem factelem f; 230 1.3 lukem FILE *dout; 231 1.3 lukem DIR *dirp; 232 1.3 lukem char name[MAXPATHLEN]; 233 1.3 lukem int hastypefact; 234 1.1 lukem 235 1.3 lukem hastypefact = facttab[FACT_TYPE].enabled; 236 1.1 lukem if (path == NULL) 237 1.1 lukem path = "."; 238 1.1 lukem if (stat(path, &pdirstat) == -1) { 239 1.1 lukem mlsdperror: 240 1.1 lukem perror_reply(550, path); 241 1.1 lukem return; 242 1.1 lukem } 243 1.1 lukem if (! S_ISDIR(pdirstat.st_mode)) { 244 1.1 lukem errno = ENOTDIR; 245 1.3 lukem perror_reply(501, path); 246 1.3 lukem return; 247 1.1 lukem } 248 1.15 lukem if ((dirp = opendir(path)) == NULL) 249 1.15 lukem goto mlsdperror; 250 1.15 lukem 251 1.1 lukem dout = dataconn("MLSD", (off_t)-1, "w"); 252 1.34 shm if (dout == NULL) { 253 1.34 shm (void) closedir(dirp); 254 1.1 lukem return; 255 1.34 shm } 256 1.1 lukem 257 1.16 lukem memset(&f, 0, sizeof(f)); 258 1.1 lukem f.stat = &sb; 259 1.16 lukem f.flags |= FE_MLSD; 260 1.1 lukem while ((dp = readdir(dirp)) != NULL) { 261 1.1 lukem snprintf(name, sizeof(name), "%s/%s", path, dp->d_name); 262 1.1 lukem if (ISDOTDIR(dp->d_name)) { /* special case curdir: */ 263 1.3 lukem if (! hastypefact) 264 1.3 lukem continue; 265 1.3 lukem f.pdirstat = NULL; /* require stat of parent */ 266 1.1 lukem f.display = path; /* set name to real name */ 267 1.16 lukem f.flags |= FE_ISCURDIR; /* flag name is curdir */ 268 1.1 lukem } else { 269 1.3 lukem if (ISDOTDOTDIR(dp->d_name)) { 270 1.3 lukem if (! hastypefact) 271 1.3 lukem continue; 272 1.3 lukem f.pdirstat = NULL; 273 1.3 lukem } else 274 1.3 lukem f.pdirstat = &pdirstat; /* cache parent stat */ 275 1.1 lukem f.display = dp->d_name; 276 1.16 lukem f.flags &= ~FE_ISCURDIR; 277 1.1 lukem } 278 1.3 lukem if (stat(name, &sb) == -1) 279 1.3 lukem continue; 280 1.3 lukem f.path = name; 281 1.1 lukem mlsname(dout, &f); 282 1.1 lukem } 283 1.1 lukem (void)closedir(dirp); 284 1.1 lukem 285 1.1 lukem if (ferror(dout) != 0) 286 1.1 lukem perror_reply(550, "Data connection"); 287 1.1 lukem else 288 1.1 lukem reply(226, "MLSD complete."); 289 1.3 lukem closedataconn(dout); 290 1.1 lukem total_xfers_out++; 291 1.1 lukem total_xfers++; 292 1.1 lukem } 293 1.1 lukem 294 1.1 lukem void 295 1.1 lukem mlst(const char *path) 296 1.1 lukem { 297 1.1 lukem struct stat sb; 298 1.1 lukem factelem f; 299 1.1 lukem 300 1.1 lukem if (path == NULL) 301 1.1 lukem path = "."; 302 1.1 lukem if (stat(path, &sb) == -1) { 303 1.1 lukem perror_reply(550, path); 304 1.1 lukem return; 305 1.1 lukem } 306 1.3 lukem reply(-250, "MLST %s", path); 307 1.16 lukem memset(&f, 0, sizeof(f)); 308 1.1 lukem f.path = path; 309 1.1 lukem f.display = path; 310 1.1 lukem f.stat = &sb; 311 1.1 lukem f.pdirstat = NULL; 312 1.3 lukem CPUTC(' ', stdout); 313 1.1 lukem mlsname(stdout, &f); 314 1.1 lukem reply(250, "End"); 315 1.1 lukem } 316 1.1 lukem 317 1.1 lukem 318 1.1 lukem void 319 1.1 lukem opts(const char *command) 320 1.1 lukem { 321 1.1 lukem struct tab *c; 322 1.1 lukem char *ep; 323 1.1 lukem 324 1.1 lukem if ((ep = strchr(command, ' ')) != NULL) 325 1.1 lukem *ep++ = '\0'; 326 1.1 lukem c = lookup(cmdtab, command); 327 1.1 lukem if (c == NULL) { 328 1.18 darrenr reply(502, "Unknown command '%s'.", command); 329 1.1 lukem return; 330 1.1 lukem } 331 1.1 lukem if (! CMD_IMPLEMENTED(c)) { 332 1.17 darrenr reply(502, "%s command not implemented.", c->name); 333 1.1 lukem return; 334 1.1 lukem } 335 1.1 lukem if (! CMD_HAS_OPTIONS(c)) { 336 1.1 lukem reply(501, "%s command does not support persistent options.", 337 1.1 lukem c->name); 338 1.1 lukem return; 339 1.1 lukem } 340 1.1 lukem 341 1.1 lukem /* special case: MLST */ 342 1.1 lukem if (strcasecmp(command, "MLST") == 0) { 343 1.4 lukem int enabled[FACTTABSIZE]; 344 1.30 lukem size_t i, onedone; 345 1.3 lukem size_t len; 346 1.1 lukem char *p; 347 1.1 lukem 348 1.3 lukem for (i = 0; i < sizeof(enabled) / sizeof(int); i++) 349 1.3 lukem enabled[i] = 0; 350 1.1 lukem if (ep == NULL || *ep == '\0') 351 1.1 lukem goto displaymlstopts; 352 1.1 lukem 353 1.1 lukem /* don't like spaces, and need trailing ; */ 354 1.3 lukem len = strlen(ep); 355 1.3 lukem if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') { 356 1.3 lukem badmlstopt: 357 1.1 lukem reply(501, "Invalid MLST options"); 358 1.1 lukem return; 359 1.1 lukem } 360 1.3 lukem ep[len - 1] = '\0'; 361 1.1 lukem while ((p = strsep(&ep, ";")) != NULL) { 362 1.3 lukem if (*p == '\0') 363 1.3 lukem goto badmlstopt; 364 1.4 lukem for (i = 0; i < FACTTABSIZE; i++) 365 1.1 lukem if (strcasecmp(p, facttab[i].name) == 0) { 366 1.3 lukem enabled[i] = 1; 367 1.1 lukem break; 368 1.1 lukem } 369 1.1 lukem } 370 1.1 lukem 371 1.1 lukem displaymlstopts: 372 1.4 lukem for (i = 0; i < FACTTABSIZE; i++) 373 1.3 lukem facttab[i].enabled = enabled[i]; 374 1.3 lukem cprintf(stdout, "200 MLST OPTS"); 375 1.4 lukem for (i = onedone = 0; i < FACTTABSIZE; i++) { 376 1.1 lukem if (facttab[i].enabled) { 377 1.3 lukem cprintf(stdout, "%s%s;", onedone ? "" : " ", 378 1.1 lukem facttab[i].name); 379 1.1 lukem onedone++; 380 1.1 lukem } 381 1.1 lukem } 382 1.3 lukem cprintf(stdout, "\r\n"); 383 1.3 lukem fflush(stdout); 384 1.1 lukem return; 385 1.1 lukem } 386 1.1 lukem 387 1.1 lukem /* default cases */ 388 1.1 lukem if (ep != NULL && *ep != '\0') 389 1.24 christos REASSIGN(c->options, ftpd_strdup(ep)); 390 1.1 lukem if (c->options != NULL) 391 1.1 lukem reply(200, "Options for %s are '%s'.", c->name, 392 1.1 lukem c->options); 393 1.1 lukem else 394 1.1 lukem reply(200, "No options defined for %s.", c->name); 395 1.1 lukem } 396 1.1 lukem 397 1.1 lukem void 398 1.1 lukem pwd(void) 399 1.1 lukem { 400 1.1 lukem char path[MAXPATHLEN]; 401 1.1 lukem 402 1.20 manu if (getcwd(path, sizeof(path) - 1) == NULL) { 403 1.20 manu if (chdir(cached_path) < 0) { 404 1.20 manu reply(550, "Can't get the current directory: %s.", 405 1.20 manu strerror(errno)); 406 1.20 manu return; 407 1.20 manu } 408 1.20 manu (void)strlcpy(path, cached_path, MAXPATHLEN); 409 1.20 manu } 410 1.20 manu replydirname(path, "is the current directory."); 411 1.1 lukem } 412 1.1 lukem 413 1.1 lukem void 414 1.1 lukem removedir(const char *name) 415 1.1 lukem { 416 1.1 lukem char *p = NULL; 417 1.1 lukem 418 1.1 lukem if (rmdir(name) < 0) { 419 1.1 lukem p = strerror(errno); 420 1.1 lukem perror_reply(550, name); 421 1.1 lukem } else 422 1.1 lukem ack("RMD"); 423 1.10 lukem logxfer("rmdir", -1, name, NULL, NULL, p); 424 1.1 lukem } 425 1.1 lukem 426 1.1 lukem char * 427 1.1 lukem renamefrom(const char *name) 428 1.1 lukem { 429 1.1 lukem struct stat st; 430 1.1 lukem 431 1.1 lukem if (stat(name, &st) < 0) { 432 1.1 lukem perror_reply(550, name); 433 1.1 lukem return (NULL); 434 1.1 lukem } 435 1.1 lukem reply(350, "File exists, ready for destination name"); 436 1.24 christos return (ftpd_strdup(name)); 437 1.1 lukem } 438 1.1 lukem 439 1.1 lukem void 440 1.1 lukem renamecmd(const char *from, const char *to) 441 1.1 lukem { 442 1.1 lukem char *p = NULL; 443 1.1 lukem 444 1.1 lukem if (rename(from, to) < 0) { 445 1.1 lukem p = strerror(errno); 446 1.1 lukem perror_reply(550, "rename"); 447 1.1 lukem } else 448 1.1 lukem ack("RNTO"); 449 1.10 lukem logxfer("rename", -1, from, to, NULL, p); 450 1.1 lukem } 451 1.1 lukem 452 1.1 lukem void 453 1.1 lukem sizecmd(const char *filename) 454 1.1 lukem { 455 1.1 lukem switch (type) { 456 1.1 lukem case TYPE_L: 457 1.10 lukem case TYPE_I: 458 1.10 lukem { 459 1.1 lukem struct stat stbuf; 460 1.1 lukem if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 461 1.1 lukem reply(550, "%s: not a plain file.", filename); 462 1.1 lukem else 463 1.7 lukem reply(213, ULLF, (ULLT)stbuf.st_size); 464 1.10 lukem break; 465 1.10 lukem } 466 1.10 lukem case TYPE_A: 467 1.10 lukem { 468 1.1 lukem FILE *fin; 469 1.1 lukem int c; 470 1.1 lukem off_t count; 471 1.1 lukem struct stat stbuf; 472 1.1 lukem fin = fopen(filename, "r"); 473 1.1 lukem if (fin == NULL) { 474 1.1 lukem perror_reply(550, filename); 475 1.1 lukem return; 476 1.1 lukem } 477 1.1 lukem if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 478 1.1 lukem reply(550, "%s: not a plain file.", filename); 479 1.1 lukem (void) fclose(fin); 480 1.1 lukem return; 481 1.1 lukem } 482 1.14 itojun if (stbuf.st_size > 10240) { 483 1.14 itojun reply(550, "%s: file too large for SIZE.", filename); 484 1.14 itojun (void) fclose(fin); 485 1.14 itojun return; 486 1.14 itojun } 487 1.1 lukem 488 1.1 lukem count = 0; 489 1.14 itojun while((c = getc(fin)) != EOF) { 490 1.1 lukem if (c == '\n') /* will get expanded to \r\n */ 491 1.1 lukem count++; 492 1.1 lukem count++; 493 1.1 lukem } 494 1.1 lukem (void) fclose(fin); 495 1.1 lukem 496 1.7 lukem reply(213, LLF, (LLT)count); 497 1.10 lukem break; 498 1.10 lukem } 499 1.1 lukem default: 500 1.1 lukem reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 501 1.1 lukem } 502 1.1 lukem } 503 1.1 lukem 504 1.1 lukem void 505 1.1 lukem statfilecmd(const char *filename) 506 1.1 lukem { 507 1.1 lukem FILE *fin; 508 1.1 lukem int c; 509 1.19 itojun int atstart; 510 1.30 lukem const char *argv[] = { INTERNAL_LS, "-lgA", "", NULL }; 511 1.1 lukem 512 1.30 lukem argv[2] = filename; 513 1.1 lukem fin = ftpd_popen(argv, "r", STDOUT_FILENO); 514 1.3 lukem reply(-211, "status of %s:", filename); 515 1.3 lukem /* XXX: use fgetln() or fparseln() here? */ 516 1.19 itojun atstart = 1; 517 1.1 lukem while ((c = getc(fin)) != EOF) { 518 1.1 lukem if (c == '\n') { 519 1.1 lukem if (ferror(stdout)){ 520 1.1 lukem perror_reply(421, "control connection"); 521 1.1 lukem (void) ftpd_pclose(fin); 522 1.1 lukem dologout(1); 523 1.1 lukem /* NOTREACHED */ 524 1.1 lukem } 525 1.1 lukem if (ferror(fin)) { 526 1.1 lukem perror_reply(551, filename); 527 1.1 lukem (void) ftpd_pclose(fin); 528 1.1 lukem return; 529 1.1 lukem } 530 1.3 lukem CPUTC('\r', stdout); 531 1.1 lukem } 532 1.19 itojun if (atstart && isdigit(c)) 533 1.19 itojun CPUTC(' ', stdout); 534 1.3 lukem CPUTC(c, stdout); 535 1.19 itojun atstart = (c == '\n'); 536 1.1 lukem } 537 1.1 lukem (void) ftpd_pclose(fin); 538 1.1 lukem reply(211, "End of Status"); 539 1.1 lukem } 540 1.1 lukem 541 1.1 lukem /* -- */ 542 1.1 lukem 543 1.1 lukem static void 544 1.1 lukem ack(const char *s) 545 1.1 lukem { 546 1.1 lukem 547 1.1 lukem reply(250, "%s command successful.", s); 548 1.1 lukem } 549 1.1 lukem 550 1.3 lukem /* 551 1.3 lukem * Encode len bytes starting at clear using base64 encoding into encoded, 552 1.3 lukem * which should be at least ((len + 2) * 4 / 3 + 1) in size. 553 1.6 lukem * If nulterm is non-zero, terminate with \0 otherwise pad to 3 byte boundary 554 1.6 lukem * with `='. 555 1.3 lukem */ 556 1.3 lukem static void 557 1.3 lukem base64_encode(const char *clear, size_t len, char *encoded, int nulterm) 558 1.3 lukem { 559 1.5 lukem static const char base64[] = 560 1.3 lukem "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 561 1.5 lukem const char *c; 562 1.5 lukem char *e, termchar; 563 1.3 lukem int i; 564 1.3 lukem 565 1.6 lukem /* determine whether to pad with '=' or NUL terminate */ 566 1.5 lukem termchar = nulterm ? '\0' : '='; 567 1.5 lukem c = clear; 568 1.5 lukem e = encoded; 569 1.6 lukem /* convert all but last 2 bytes */ 570 1.6 lukem for (i = len; i > 2; i -= 3, c += 3) { 571 1.6 lukem *e++ = base64[(c[0] >> 2) & 0x3f]; 572 1.6 lukem *e++ = base64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)]; 573 1.6 lukem *e++ = base64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)]; 574 1.6 lukem *e++ = base64[(c[2]) & 0x3f]; 575 1.6 lukem } 576 1.6 lukem /* handle slop at end */ 577 1.6 lukem if (i > 0) { 578 1.6 lukem *e++ = base64[(c[0] >> 2) & 0x3f]; 579 1.5 lukem *e++ = base64[((c[0] << 4) & 0x30) | 580 1.6 lukem (i > 1 ? ((c[1] >> 4) & 0x0f) : 0)]; 581 1.6 lukem *e++ = (i > 1) ? base64[(c[1] << 2) & 0x3c] : termchar; 582 1.6 lukem *e++ = termchar; 583 1.5 lukem } 584 1.5 lukem *e = '\0'; 585 1.3 lukem } 586 1.3 lukem 587 1.1 lukem static void 588 1.1 lukem fact_modify(const char *fact, FILE *fd, factelem *fe) 589 1.1 lukem { 590 1.1 lukem struct tm *t; 591 1.1 lukem 592 1.1 lukem t = gmtime(&(fe->stat->st_mtime)); 593 1.3 lukem cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact, 594 1.1 lukem TM_YEAR_BASE + t->tm_year, 595 1.1 lukem t->tm_mon+1, t->tm_mday, 596 1.1 lukem t->tm_hour, t->tm_min, t->tm_sec); 597 1.1 lukem } 598 1.1 lukem 599 1.1 lukem static void 600 1.1 lukem fact_perm(const char *fact, FILE *fd, factelem *fe) 601 1.1 lukem { 602 1.3 lukem int rok, wok, xok, pdirwok; 603 1.35 christos struct stat *pdir, dir; 604 1.1 lukem 605 1.1 lukem if (fe->stat->st_uid == geteuid()) { 606 1.1 lukem rok = ((fe->stat->st_mode & S_IRUSR) != 0); 607 1.1 lukem wok = ((fe->stat->st_mode & S_IWUSR) != 0); 608 1.1 lukem xok = ((fe->stat->st_mode & S_IXUSR) != 0); 609 1.3 lukem } else if (matchgroup(fe->stat->st_gid)) { 610 1.1 lukem rok = ((fe->stat->st_mode & S_IRGRP) != 0); 611 1.1 lukem wok = ((fe->stat->st_mode & S_IWGRP) != 0); 612 1.1 lukem xok = ((fe->stat->st_mode & S_IXGRP) != 0); 613 1.1 lukem } else { 614 1.1 lukem rok = ((fe->stat->st_mode & S_IROTH) != 0); 615 1.1 lukem wok = ((fe->stat->st_mode & S_IWOTH) != 0); 616 1.1 lukem xok = ((fe->stat->st_mode & S_IXOTH) != 0); 617 1.1 lukem } 618 1.1 lukem 619 1.3 lukem cprintf(fd, "%s=", fact); 620 1.1 lukem 621 1.1 lukem /* 622 1.1 lukem * if parent info not provided, look it up, but 623 1.1 lukem * only if the current class has modify rights, 624 1.1 lukem * since we only need this info in such a case. 625 1.1 lukem */ 626 1.1 lukem pdir = fe->pdirstat; 627 1.8 lukem if (pdir == NULL && CURCLASS_FLAGS_ISSET(modify)) { 628 1.1 lukem size_t len; 629 1.1 lukem char realdir[MAXPATHLEN], *p; 630 1.1 lukem 631 1.1 lukem len = strlcpy(realdir, fe->path, sizeof(realdir)); 632 1.1 lukem if (len < sizeof(realdir) - 4) { 633 1.1 lukem if (S_ISDIR(fe->stat->st_mode)) 634 1.1 lukem strlcat(realdir, "/..", sizeof(realdir)); 635 1.1 lukem else { 636 1.1 lukem /* if has a /, move back to it */ 637 1.1 lukem /* otherwise use '..' */ 638 1.1 lukem if ((p = strrchr(realdir, '/')) != NULL) { 639 1.1 lukem if (p == realdir) 640 1.1 lukem p++; 641 1.1 lukem *p = '\0'; 642 1.1 lukem } else 643 1.1 lukem strlcpy(realdir, "..", sizeof(realdir)); 644 1.1 lukem } 645 1.1 lukem if (stat(realdir, &dir) == 0) 646 1.1 lukem pdir = &dir; 647 1.1 lukem } 648 1.1 lukem } 649 1.1 lukem pdirwok = 0; 650 1.1 lukem if (pdir != NULL) { 651 1.1 lukem if (pdir->st_uid == geteuid()) 652 1.1 lukem pdirwok = ((pdir->st_mode & S_IWUSR) != 0); 653 1.3 lukem else if (matchgroup(pdir->st_gid)) 654 1.1 lukem pdirwok = ((pdir->st_mode & S_IWGRP) != 0); 655 1.1 lukem else 656 1.1 lukem pdirwok = ((pdir->st_mode & S_IWOTH) != 0); 657 1.1 lukem } 658 1.1 lukem 659 1.1 lukem /* 'a': can APPE to file */ 660 1.8 lukem if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 661 1.3 lukem CPUTC('a', fd); 662 1.1 lukem 663 1.1 lukem /* 'c': can create or append to files in directory */ 664 1.8 lukem if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 665 1.3 lukem CPUTC('c', fd); 666 1.1 lukem 667 1.1 lukem /* 'd': can delete file or directory */ 668 1.8 lukem if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) { 669 1.1 lukem int candel; 670 1.1 lukem 671 1.1 lukem candel = 1; 672 1.1 lukem if (S_ISDIR(fe->stat->st_mode)) { 673 1.1 lukem DIR *dirp; 674 1.1 lukem struct dirent *dp; 675 1.1 lukem 676 1.1 lukem if ((dirp = opendir(fe->display)) == NULL) 677 1.1 lukem candel = 0; 678 1.1 lukem else { 679 1.1 lukem while ((dp = readdir(dirp)) != NULL) { 680 1.1 lukem if (ISDOTDIR(dp->d_name) || 681 1.1 lukem ISDOTDOTDIR(dp->d_name)) 682 1.1 lukem continue; 683 1.1 lukem candel = 0; 684 1.1 lukem break; 685 1.1 lukem } 686 1.1 lukem closedir(dirp); 687 1.1 lukem } 688 1.1 lukem } 689 1.1 lukem if (candel) 690 1.3 lukem CPUTC('d', fd); 691 1.1 lukem } 692 1.1 lukem 693 1.1 lukem /* 'e': can enter directory */ 694 1.1 lukem if (xok && S_ISDIR(fe->stat->st_mode)) 695 1.3 lukem CPUTC('e', fd); 696 1.1 lukem 697 1.1 lukem /* 'f': can rename file or directory */ 698 1.8 lukem if (pdirwok && CURCLASS_FLAGS_ISSET(modify)) 699 1.3 lukem CPUTC('f', fd); 700 1.1 lukem 701 1.1 lukem /* 'l': can list directory */ 702 1.1 lukem if (rok && xok && S_ISDIR(fe->stat->st_mode)) 703 1.3 lukem CPUTC('l', fd); 704 1.1 lukem 705 1.1 lukem /* 'm': can create directory */ 706 1.8 lukem if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 707 1.3 lukem CPUTC('m', fd); 708 1.1 lukem 709 1.1 lukem /* 'p': can remove files in directory */ 710 1.8 lukem if (wok && CURCLASS_FLAGS_ISSET(modify) && S_ISDIR(fe->stat->st_mode)) 711 1.3 lukem CPUTC('p', fd); 712 1.1 lukem 713 1.1 lukem /* 'r': can RETR file */ 714 1.1 lukem if (rok && S_ISREG(fe->stat->st_mode)) 715 1.3 lukem CPUTC('r', fd); 716 1.1 lukem 717 1.1 lukem /* 'w': can STOR file */ 718 1.8 lukem if (wok && CURCLASS_FLAGS_ISSET(upload) && S_ISREG(fe->stat->st_mode)) 719 1.3 lukem CPUTC('w', fd); 720 1.1 lukem 721 1.3 lukem CPUTC(';', fd); 722 1.1 lukem } 723 1.1 lukem 724 1.1 lukem static void 725 1.1 lukem fact_size(const char *fact, FILE *fd, factelem *fe) 726 1.1 lukem { 727 1.1 lukem 728 1.1 lukem if (S_ISREG(fe->stat->st_mode)) 729 1.7 lukem cprintf(fd, "%s=" LLF ";", fact, (LLT)fe->stat->st_size); 730 1.1 lukem } 731 1.1 lukem 732 1.1 lukem static void 733 1.1 lukem fact_type(const char *fact, FILE *fd, factelem *fe) 734 1.1 lukem { 735 1.1 lukem 736 1.3 lukem cprintf(fd, "%s=", fact); 737 1.1 lukem switch (fe->stat->st_mode & S_IFMT) { 738 1.1 lukem case S_IFDIR: 739 1.16 lukem if (fe->flags & FE_MLSD) { 740 1.16 lukem if ((fe->flags & FE_ISCURDIR) || ISDOTDIR(fe->display)) 741 1.16 lukem cprintf(fd, "cdir"); 742 1.16 lukem else if (ISDOTDOTDIR(fe->display)) 743 1.16 lukem cprintf(fd, "pdir"); 744 1.16 lukem else 745 1.16 lukem cprintf(fd, "dir"); 746 1.16 lukem } else { 747 1.3 lukem cprintf(fd, "dir"); 748 1.16 lukem } 749 1.1 lukem break; 750 1.1 lukem case S_IFREG: 751 1.3 lukem cprintf(fd, "file"); 752 1.1 lukem break; 753 1.1 lukem case S_IFIFO: 754 1.3 lukem cprintf(fd, "OS.unix=fifo"); 755 1.1 lukem break; 756 1.3 lukem case S_IFLNK: /* XXX: probably a NO-OP with stat() */ 757 1.3 lukem cprintf(fd, "OS.unix=slink"); 758 1.1 lukem break; 759 1.1 lukem case S_IFSOCK: 760 1.3 lukem cprintf(fd, "OS.unix=socket"); 761 1.1 lukem break; 762 1.1 lukem case S_IFBLK: 763 1.1 lukem case S_IFCHR: 764 1.29 lukem cprintf(fd, "OS.unix=%s-" ULLF "/" ULLF, 765 1.1 lukem S_ISBLK(fe->stat->st_mode) ? "blk" : "chr", 766 1.29 lukem (ULLT)major(fe->stat->st_rdev), 767 1.29 lukem (ULLT)minor(fe->stat->st_rdev)); 768 1.1 lukem break; 769 1.1 lukem default: 770 1.3 lukem cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT); 771 1.1 lukem break; 772 1.1 lukem } 773 1.3 lukem CPUTC(';', fd); 774 1.1 lukem } 775 1.1 lukem 776 1.1 lukem static void 777 1.1 lukem fact_unique(const char *fact, FILE *fd, factelem *fe) 778 1.1 lukem { 779 1.5 lukem char obuf[(sizeof(dev_t) + sizeof(ino_t) + 2) * 4 / 3 + 2]; 780 1.5 lukem char tbuf[sizeof(dev_t) + sizeof(ino_t)]; 781 1.1 lukem 782 1.5 lukem memcpy(tbuf, 783 1.5 lukem (char *)&(fe->stat->st_dev), sizeof(dev_t)); 784 1.5 lukem memcpy(tbuf + sizeof(dev_t), 785 1.5 lukem (char *)&(fe->stat->st_ino), sizeof(ino_t)); 786 1.5 lukem base64_encode(tbuf, sizeof(dev_t) + sizeof(ino_t), obuf, 1); 787 1.5 lukem cprintf(fd, "%s=%s;", fact, obuf); 788 1.1 lukem } 789 1.1 lukem 790 1.1 lukem static int 791 1.3 lukem matchgroup(gid_t gid) 792 1.1 lukem { 793 1.1 lukem int i; 794 1.1 lukem 795 1.3 lukem for (i = 0; i < gidcount; i++) 796 1.1 lukem if (gid == gidlist[i]) 797 1.1 lukem return(1); 798 1.1 lukem return (0); 799 1.1 lukem } 800 1.1 lukem 801 1.1 lukem static void 802 1.1 lukem mlsname(FILE *fp, factelem *fe) 803 1.1 lukem { 804 1.16 lukem char realfile[MAXPATHLEN]; 805 1.30 lukem int userf = 0; 806 1.30 lukem size_t i; 807 1.1 lukem 808 1.4 lukem for (i = 0; i < FACTTABSIZE; i++) { 809 1.1 lukem if (facttab[i].enabled) 810 1.1 lukem (facttab[i].display)(facttab[i].name, fp, fe); 811 1.1 lukem } 812 1.16 lukem if ((fe->flags & FE_MLSD) && 813 1.16 lukem !(fe->flags & FE_ISCURDIR) && !ISDOTDIR(fe->display)) { 814 1.16 lukem /* if MLSD and not "." entry, display as-is */ 815 1.16 lukem userf = 0; 816 1.16 lukem } else { 817 1.16 lukem /* if MLST, or MLSD and "." entry, realpath(3) it */ 818 1.16 lukem if (realpath(fe->display, realfile) != NULL) 819 1.16 lukem userf = 1; 820 1.16 lukem } 821 1.16 lukem cprintf(fp, " %s\r\n", userf ? realfile : fe->display); 822 1.1 lukem } 823 1.1 lukem 824 1.1 lukem static void 825 1.1 lukem replydirname(const char *name, const char *message) 826 1.1 lukem { 827 1.9 itojun char *p, *ep; 828 1.12 itojun char npath[MAXPATHLEN * 2]; 829 1.1 lukem 830 1.9 itojun p = npath; 831 1.9 itojun ep = &npath[sizeof(npath) - 1]; 832 1.9 itojun while (*name) { 833 1.11 itojun if (*name == '"') { 834 1.11 itojun if (ep - p < 2) 835 1.11 itojun break; 836 1.9 itojun *p++ = *name++; 837 1.9 itojun *p++ = '"'; 838 1.11 itojun } else { 839 1.11 itojun if (ep - p < 1) 840 1.11 itojun break; 841 1.9 itojun *p++ = *name++; 842 1.11 itojun } 843 1.1 lukem } 844 1.9 itojun *p = '\0'; 845 1.1 lukem reply(257, "\"%s\" %s", npath, message); 846 1.1 lukem } 847 1.20 manu 848 1.20 manu static void 849 1.31 dholland discover_path(char *last_path, const char *new_path) 850 1.20 manu { 851 1.20 manu char tp[MAXPATHLEN + 1] = ""; 852 1.20 manu char tq[MAXPATHLEN + 1] = ""; 853 1.20 manu char *cp; 854 1.20 manu char *cq; 855 1.20 manu int sz1, sz2; 856 1.20 manu int nomorelink; 857 1.20 manu struct stat st1, st2; 858 1.20 manu 859 1.20 manu if (new_path[0] != '/') { 860 1.20 manu (void)strlcpy(tp, last_path, MAXPATHLEN); 861 1.20 manu (void)strlcat(tp, "/", MAXPATHLEN); 862 1.20 manu } 863 1.20 manu (void)strlcat(tp, new_path, MAXPATHLEN); 864 1.20 manu (void)strlcat(tp, "/", MAXPATHLEN); 865 1.20 manu 866 1.20 manu /* 867 1.20 manu * resolve symlinks. A symlink may introduce another symlink, so we 868 1.20 manu * loop trying to resolve symlinks until we don't find any of them. 869 1.20 manu */ 870 1.20 manu do { 871 1.20 manu /* Collapse any // into / */ 872 1.20 manu while ((cp = strstr(tp, "//")) != NULL) 873 1.20 manu (void)memmove(cp, cp + 1, strlen(cp) - 1 + 1); 874 1.20 manu 875 1.20 manu /* Collapse any /./ into / */ 876 1.20 manu while ((cp = strstr(tp, "/./")) != NULL) 877 1.20 manu (void)memmove(cp, cp + 2, strlen(cp) - 2 + 1); 878 1.20 manu 879 1.20 manu cp = tp; 880 1.20 manu nomorelink = 1; 881 1.20 manu 882 1.32 joerg while ((cp = strstr(cp + 1, "/")) != NULL) { 883 1.26 lukem sz1 = (unsigned long)cp - (unsigned long)tp; 884 1.20 manu if (sz1 > MAXPATHLEN) 885 1.20 manu goto bad; 886 1.20 manu *cp = 0; 887 1.20 manu sz2 = readlink(tp, tq, MAXPATHLEN); 888 1.20 manu *cp = '/'; 889 1.20 manu 890 1.20 manu /* If this is not a symlink, move to next / */ 891 1.20 manu if (sz2 <= 0) 892 1.20 manu continue; 893 1.20 manu 894 1.20 manu /* 895 1.20 manu * We found a symlink, so we will have to 896 1.20 manu * do one more pass to check there is no 897 1.20 manu * more symlink in the path 898 1.20 manu */ 899 1.20 manu nomorelink = 0; 900 1.20 manu 901 1.20 manu /* 902 1.20 manu * Null terminate the string and remove trailing / 903 1.20 manu */ 904 1.20 manu tq[sz2] = 0; 905 1.20 manu sz2 = strlen(tq); 906 1.20 manu if (tq[sz2 - 1] == '/') 907 1.20 manu tq[--sz2] = 0; 908 1.20 manu 909 1.20 manu /* 910 1.20 manu * Is this an absolute link or a relative link? 911 1.20 manu */ 912 1.20 manu if (tq[0] == '/') { 913 1.20 manu /* absolute link */ 914 1.20 manu if (strlen(cp) + sz2 > MAXPATHLEN) 915 1.20 manu goto bad; 916 1.20 manu memmove(tp + sz2, cp, strlen(cp) + 1); 917 1.20 manu memcpy(tp, tq, sz2); 918 1.20 manu } else { 919 1.20 manu /* relative link */ 920 1.20 manu for (cq = cp - 1; *cq != '/'; cq--); 921 1.26 lukem if (strlen(tp) - 922 1.26 lukem ((unsigned long)cq - (unsigned long)cp) 923 1.20 manu + 1 + sz2 > MAXPATHLEN) 924 1.20 manu goto bad; 925 1.20 manu (void)memmove(cq + 1 + sz2, 926 1.20 manu cp, strlen(cp) + 1); 927 1.20 manu (void)memcpy(cq + 1, tq, sz2); 928 1.20 manu } 929 1.20 manu 930 1.20 manu /* 931 1.20 manu * start over, looking for new symlinks 932 1.20 manu */ 933 1.20 manu break; 934 1.20 manu } 935 1.20 manu } while (nomorelink == 0); 936 1.20 manu 937 1.20 manu /* Collapse any /foo/../ into /foo/ */ 938 1.20 manu while ((cp = strstr(tp, "/../")) != NULL) { 939 1.20 manu /* ^/../foo/ becomes ^/foo/ */ 940 1.20 manu if (cp == tp) { 941 1.20 manu (void)memmove(cp, cp + 3, 942 1.20 manu strlen(cp) - 3 + 1); 943 1.20 manu } else { 944 1.20 manu for (cq = cp - 1; *cq != '/'; cq--); 945 1.20 manu (void)memmove(cq, cp + 3, 946 1.20 manu strlen(cp) - 3 + 1); 947 1.20 manu } 948 1.20 manu } 949 1.20 manu 950 1.20 manu /* strip strailing / */ 951 1.20 manu if (strlen(tp) != 1) 952 1.20 manu tp[strlen(tp) - 1] = '\0'; 953 1.20 manu 954 1.20 manu /* check that the path is correct */ 955 1.33 christos if (stat(tp, &st1) == -1 || stat(".", &st2) == -1) 956 1.33 christos goto bad; 957 1.20 manu if ((st1.st_dev != st2.st_dev) || (st1.st_ino != st2.st_ino)) 958 1.20 manu goto bad; 959 1.20 manu 960 1.20 manu (void)strlcpy(last_path, tp, MAXPATHLEN); 961 1.20 manu return; 962 1.20 manu 963 1.20 manu bad: 964 1.20 manu (void)strlcat(last_path, "/", MAXPATHLEN); 965 1.20 manu (void)strlcat(last_path, new_path, MAXPATHLEN); 966 1.20 manu return; 967 1.20 manu } 968 1.20 manu 969