/* ** Copyright 2000-2004 University of Illinois Board of Trustees ** Copyright 2000-2004 Mark D. Roth ** All rights reserved. ** ** ftpstat.c - FTP file attribute checking code ** ** Mark D. Roth */ #include #include #include #include #include #ifdef STDC_HEADERS # include #endif static int _ftp_stat_aux(FTP *ftp, char *path, struct ftpstat *fsp, unsigned short flags) { file_info_t *fip = NULL; #ifdef DEBUG printf("==> _ftp_stat_aux(ftp=0x%lx, path=\"%s\", fsp=0x%lx, " "flags=%hu)\n", ftp, path, fsp, flags); #endif if (_ftp_dircache_find_file(ftp, path, flags, &fip) == -1 || fip == NULL) return -1; memcpy(fsp, &(fip->fi_stat), sizeof(struct ftpstat)); return 0; } /* stat a symlink */ int ftp_lstat(FTP *ftp, char *path, struct ftpstat *fsp) { return _ftp_stat_aux(ftp, path, fsp, DC_NOFOLLOWLINKS); } /* stat a file, dereferencing symlinks */ int ftp_stat(FTP *ftp, char *path, struct ftpstat *fsp) { return _ftp_stat_aux(ftp, path, fsp, 0); } /* read the contents of a symbolic link */ int ftp_readlink(FTP *ftp, char *path, char *buf, size_t bufsize) { file_info_t *fip = NULL; if (_ftp_dircache_find_file(ftp, path, DC_NOFOLLOWLINKS, &fip) == -1 || fip == NULL) return -1; if (! S_ISLNK(fip->fi_stat.fs_mode)) { errno = EINVAL; return -1; } strlcpy(buf, fip->fi_linkto, bufsize); return 0; } /* determine the absolute pathname of the file or dir specified by path */ int _ftp_abspath(FTP *ftp, char *path, char *abspath, size_t abspathlen) { char buf[MAXPATHLEN]; if (path[0] == '/') strlcpy(buf, path, sizeof(buf)); else snprintf(buf, sizeof(buf), "%s/%s", ftp_getcwd(ftp), path); return fget_cleanpath(buf, abspath, abspathlen); } /* resolve symlinks */ static int _ftp_realpath_aux(FTP *ftp, char *path, char *resolved_path, size_t resolvelen, int level) { char pathbuf[MAXPATHLEN]; char buf[MAXPATHLEN]; char link_target[MAXPATHLEN]; char *nextp, *dirp = NULL; struct ftpstat fs; #ifdef DEBUG printf("==> _ftp_realpath_aux(ftp=(%s), path=\"%s\", " "resolved_path=0x%lx, resolvelen=%d, level=%d)\n", ftp->ftp_host, path, resolved_path, resolvelen, level); #endif if (level >= MAXSYMLINKS) { errno = ELOOP; return -1; } /* initialize to empty string */ resolved_path[0] = '\0'; _ftp_abspath(ftp, path, pathbuf, sizeof(pathbuf)); nextp = pathbuf + 1; /* skip leading slash */ /* iterate through path components */ while ((dirp = strsep(&nextp, "/")) != NULL) { strlcat(resolved_path, "/", resolvelen); strlcat(resolved_path, dirp, resolvelen); #ifdef DEBUG printf("dirp=\"%s\"\n", dirp); printf("nextp=\"%s\"\n", nextp ? nextp : "[NULL]"); printf("resolved_path=\"%s\"\n", resolved_path); #endif if (ftp_lstat(ftp, resolved_path, &fs) == -1) return -1; if (S_ISLNK(fs.fs_mode)) { if (ftp_readlink(ftp, resolved_path, link_target, sizeof(link_target)) == -1) return -1; /* ** determine absolute path that the link points to */ if (link_target[0] != '/') snprintf(buf, sizeof(buf), "%s/%s", dirname(path), link_target); else strlcpy(buf, link_target, sizeof(buf)); /* ** append the remaining components from the ** original path */ if (nextp != NULL) { strlcat(buf, "/", sizeof(buf)); strlcat(buf, nextp, sizeof(buf)); } /* ** call ourself recursively */ return _ftp_realpath_aux(ftp, buf, resolved_path, resolvelen, level + 1); } } #ifdef DEBUG printf("<== _ftp_realpath_aux(): returning \"%s\"\n", resolved_path); #endif return 0; } int ftp_realpath(FTP *ftp, char *path, char *resolved_path, size_t resolvelen) { return _ftp_realpath_aux(ftp, path, resolved_path, resolvelen, 0); }