#include #include #include #include #include #include #include #include #include #include #include #include #include "idmap.h" #include "ls.h" struct fileInfo { char * name; struct stat sb; }; static void permsString(int mode, char * perms); static int statFile(char * dir, char * fn, int flags, struct stat * sbp); static int implicitListFile(int sock, char * path, char * fn, struct stat * sbp, int flags); static int nameCmp(const void * a, const void * b); static int sizeCmp(const void * a, const void * b); static int mtimeCmp(const void * a, const void * b); static void multicolumnListing(int sock, struct fileInfo * files, int filesCount, int flags); static int sendDirContents(int sock, char * path, char * fn, int flags); static void permsString(int mode, char * perms) { strcpy(perms, "----------"); if (mode & S_ISVTX) perms[9] = 't'; if (mode & S_IRUSR) perms[1] = 'r'; if (mode & S_IWUSR) perms[2] = 'w'; if (mode & S_IXUSR) perms[3] = 'x'; if (mode & S_IRGRP) perms[4] = 'r'; if (mode & S_IWGRP) perms[5] = 'w'; if (mode & S_IXGRP) perms[6] = 'x'; if (mode & S_IROTH) perms[7] = 'r'; if (mode & S_IWOTH) perms[8] = 'w'; if (mode & S_IXOTH) perms[9] = 'x'; if (mode & S_ISUID) { if (mode & S_IXUSR) perms[3] = 's'; else perms[3] = 'S'; } if (mode & S_ISGID) { if (mode & S_IXGRP) perms[6] = 's'; else perms[6] = 'S'; } if (S_ISDIR(mode)) perms[0] = 'd'; else if (S_ISLNK(mode)) { perms[0] = 'l'; } else if (S_ISFIFO(mode)) perms[0] = 'p'; else if (S_ISSOCK(mode)) perms[0] = 'l'; else if (S_ISCHR(mode)) { perms[0] = 'c'; } else if (S_ISBLK(mode)) { perms[0] = 'b'; } } static int statFile(char * dir, char * fn, int flags, struct stat * sbp) { char * filename; if (dir) { filename = alloca(strlen(dir) + strlen(fn) + 2); sprintf(filename, "%s/%s", dir, fn); } else filename = fn; if (!(flags & SENDDIR_FOLLOWLINKS) || stat(filename, sbp)) { if (lstat(filename, sbp)) { return 1; } } return 0; } char * fileStatStr(char * dir, char * fn, struct stat * sbp, int flags) { char * info; char perms[12]; char sizefield[15]; char ownerfield[9], groupfield[9]; char timefield[20] = ""; char * linkto; char * namefield = fn; time_t themtime; time_t currenttime; char * name; int thisYear = 0; int thisMonth = 0; struct tm * tstruct; int i; char * filename; if (!sbp) { sbp = alloca(sizeof(*sbp)); if (statFile(dir, fn, flags, sbp)) return NULL; } permsString(sbp->st_mode, perms); currenttime = time(NULL); tstruct = localtime(¤ttime); thisYear = tstruct->tm_year; thisMonth = tstruct->tm_mon; name = idSearchByUid(sbp->st_uid); if (name) sprintf(ownerfield, "%-8s", name); else sprintf(ownerfield, "%-8d", (int) sbp->st_uid); name = idSearchByGid(sbp->st_gid); if (name) sprintf(groupfield, "%-8s", name); else sprintf(groupfield, "%-8d", (int) sbp->st_gid); if (S_ISLNK(sbp->st_mode)) { /* they don't reall want to see "opt -> /usr/opt@" */ linkto = alloca(1024); strcpy(linkto, "(link)"); filename = alloca(strlen(dir) + strlen(fn) + 2); sprintf(filename, "%s/%s", dir, fn); i = readlink(filename, linkto, 1023); if (i < 1) strcpy(linkto, "(cannot read symlink)"); else linkto[i] = 0; namefield = alloca(strlen(fn) + strlen(linkto) + 10); sprintf(namefield, "%s -> %s", fn, linkto); sprintf(sizefield, "%d", i); } else if (S_ISCHR(sbp->st_mode)) { perms[0] = 'c'; sprintf(sizefield, "%3d, %3d", major(sbp->st_rdev), minor(sbp->st_rdev)); } else if (S_ISBLK(sbp->st_mode)) { perms[0] = 'b'; sprintf(sizefield, "%3d, %3d", major(sbp->st_rdev), minor(sbp->st_rdev)); } else { sprintf(sizefield, "%8ld", sbp->st_size); } /* this is important if sizeof(int_32) ! sizeof(time_t) */ themtime = sbp->st_mtime; tstruct = localtime(&themtime); if (tstruct->tm_year == thisYear || ((tstruct->tm_year + 1) == thisYear && tstruct->tm_mon > thisMonth)) strftime(timefield, sizeof(timefield) - 1, "%b %d %H:%M", tstruct); else strftime(timefield, sizeof(timefield) - 1, "%b %d %Y", tstruct); info = malloc(strlen(namefield) + strlen(timefield) + 85); sprintf(info, "%s %3d %8s %8s %8s %s %s", perms, (int) sbp->st_nlink, ownerfield, groupfield, sizefield, timefield, namefield); return info; } /* Like listFiles(), but don't explode directories or wildcards */ static int implicitListFile(int sock, char * path, char * fn, struct stat * sbp, int flags) { char * info; char fileType; if (flags & SENDDIR_LONG) { info = fileStatStr(path, fn, sbp, flags); if (info) { write(sock, info, strlen(info)); free(info); } } else { write(sock, fn, strlen(fn)); } if (flags & SENDDIR_FILETYPE) { if (S_ISSOCK(sbp->st_mode)) { fileType = '='; } else if (S_ISFIFO(sbp->st_mode)) { fileType = '|'; } else if (S_ISDIR(sbp->st_mode)) { fileType = '/'; } else if (S_IRWXO & sbp->st_mode) { fileType = '*'; } else { fileType = '\0'; } if (fileType) write(sock, &fileType, 1); } write(sock, "\r\n", 2); return 0; } static int nameCmp(const void * a, const void * b) { const struct fileInfo * one = a; const struct fileInfo * two = b; return (strcmp(one->name, two->name)); } static int sizeCmp(const void * a, const void * b) { const struct fileInfo * one = a; const struct fileInfo * two = b; /* list newer files first */ if (one->sb.st_size < two->sb.st_size) return 1; else if (one->sb.st_size > two->sb.st_size) return -1; return 0; } static int mtimeCmp(const void * a, const void * b) { const struct fileInfo * one = a; const struct fileInfo * two = b; if (one->sb.st_mtime < two->sb.st_mtime) return -1; else if (one->sb.st_mtime > two->sb.st_mtime) return 1; return 0; } static void multicolumnListing(int sock, struct fileInfo * files, int filesCount, int flags) { int i, j, k; int maxWidth = 0; char format[20]; char * fileType = " "; char * buf, * name = NULL; int rows, columns; if (!filesCount) return; for (i = 0; i < filesCount; i++) { j = strlen(files[i].name); if (j > maxWidth) maxWidth = j; } maxWidth += 3; buf = alloca(maxWidth); if (flags & SENDDIR_FILETYPE) name = alloca(maxWidth); columns = 80 / maxWidth; if (columns == 0) columns = 1; sprintf(format, "%%-%ds", 80 / columns); rows = filesCount / columns; if (filesCount % columns) rows++; for (i = 0; i < rows; i++) { j = i; while (j < filesCount) { if (flags & SENDDIR_FILETYPE) { if (S_ISDIR(files[j].sb.st_mode)) fileType = "/"; else if (S_ISSOCK(files[j].sb.st_mode)) fileType = "="; else if (S_ISFIFO(files[j].sb.st_mode)) fileType = "|"; else if (S_ISLNK(files[j].sb.st_mode)) fileType = "@"; else fileType = " "; strcpy(name, files[j].name); strcat(name, fileType); } else name = files[j].name; if ((j + rows) < filesCount) k = sprintf(buf, format, name); else k = sprintf(buf, "%s", name); j += rows; write(sock, buf, k); } write(sock, "\r\n", 2); } } static int sendDirContents(int sock, char * path, char * fn, int flags) { struct dirent * ent; int start, direction; DIR * dir; int filesAlloced, filesCount, i; struct fileInfo * files, * newfiles; int failed = 0; int total = 0; char buf[20]; char * fullpath; char * subdir; filesAlloced = 15; filesCount = 0; files = malloc(sizeof(*files) * filesAlloced); if (fn) { fullpath = alloca(strlen(path) + strlen(fn) + 2); sprintf(fullpath, "%s/%s", path, fn); } else fullpath = path; dir = opendir(fullpath); do { errno = 0; ent = readdir(dir); if (errno) { fprintf(stderr, "Error reading directory entry: %s", strerror(errno)); failed = 1; } else if (ent && (*ent->d_name != '.' || (flags & SENDDIR_ALL))) { if (filesCount == filesAlloced) { filesAlloced += 15; newfiles = realloc(files, sizeof(*files) * filesAlloced); files = newfiles; } if (!failed) { files[filesCount].name = strdup(ent->d_name); if (statFile(fullpath, files[filesCount].name, flags, &files[filesCount].sb)) { fprintf(stderr, "stat of %s failed: %s" , files[filesCount].name, strerror(errno)); failed = 1; } else { total += files[filesCount].sb.st_size / 1024; } filesCount++; } } } while (ent && !failed); closedir(dir); if (!failed) { if (flags & SENDDIR_SORTMTIME) { qsort(files, filesCount, sizeof(*files), mtimeCmp); } else if (flags & SENDDIR_SORTSIZE) { qsort(files, filesCount, sizeof(*files), sizeCmp); } else if (!(flags & SENDDIR_SORTNONE)) { qsort(files, filesCount, sizeof(*files), nameCmp); } if (flags & SENDDIR_SORTREVERSE) { direction = -1; start = filesCount - 1; } else { direction = 1; start = 0; } if (fn) { write(sock, fn, strlen(fn)); write(sock, ":\r\n", 3); } if (flags & SENDDIR_MULTICOLUMN) { multicolumnListing(sock, files, filesCount, flags); } else { if (flags & SENDDIR_LONG) { i = sprintf(buf, "total %d\r\n", total); write(sock, buf, i); } for (i = start; i >= 0 && i < filesCount; i += direction) { implicitListFile(sock, fullpath, files[i].name, &files[i].sb, flags); } } if (flags & SENDDIR_RECURSE) { for (i = start; i >= 0 && i < filesCount && !failed; i += direction) { if (S_ISDIR(files[i].sb.st_mode) && strcmp(files[i].name, ".") && strcmp(files[i].name, "..")) { write(sock, "\r\n", 2); if (fn) { subdir = malloc(strlen(fn) + strlen(files[i].name) + 2); sprintf(subdir, "%s/%s", fn, files[i].name); } else { subdir = files[i].name; } failed = sendDirContents(sock, path, subdir, flags); if (fn) free(subdir); } } } } for (i = 0; i < filesCount; i++) { free(files[i].name); } free(files); return failed; } /* implements 'ls' */ void listFiles(char * path, char * fn, int flags) { struct stat sb; int i, rc; char * filename, * this; int isExplicit = 1; glob_t matches; int failed = 0; if (!fn) { fn = "."; isExplicit = 0; } filename = malloc(strlen(fn) + strlen(path) + 2); sprintf(filename, "%s/%s", path, fn); rc = glob(filename, GLOB_NOSORT, NULL, &matches); if (rc == GLOB_NOMATCH) { fprintf(stderr, "File not found."); return; } free(filename); for (i = 0; i < matches.gl_pathc && !failed; i++) { this = matches.gl_pathv[i] + strlen(path); if (*this) this++; else this = "."; if (!statFile(path, this, flags, &sb)) { if (S_ISDIR(sb.st_mode) && !(flags & SENDDIR_SIMPLEDIRS)) { filename = malloc(strlen(path) + strlen(this) + 2); sprintf(filename, "%s/%s", path, this); failed = sendDirContents(1, filename, NULL, flags); free(filename); } else { implicitListFile(1, path, this, &sb, flags); } } else { write(1, matches.gl_pathv[i], strlen(matches.gl_pathv[i])); write(1, ": file not found.\r\n", 19); } } globfree(&matches); close(1); }