/* * Copyright (c) 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of KTH nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ftpd_locl.h" RCSID("$Id: ls.c,v 1.13 1999/11/20 20:49:41 assar Exp $"); struct fileinfo { struct stat st; int inode; int bsize; char mode[11]; int n_link; char *user; char *group; char *size; char *major; char *minor; char *date; char *filename; char *link; }; #define LS_DIRS 1 #define LS_IGNORE_DOT 2 #define LS_SORT_MODE 12 #define SORT_MODE(f) ((f) & LS_SORT_MODE) #define LS_SORT_NAME 4 #define LS_SORT_MTIME 8 #define LS_SORT_SIZE 12 #define LS_SORT_REVERSE 16 #define LS_SIZE 32 #define LS_INODE 64 #ifndef S_ISTXT #define S_ISTXT S_ISVTX #endif #ifndef S_ISSOCK #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK) #endif #ifndef S_ISLNK #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK) #endif static void make_fileinfo(const char *filename, struct fileinfo *file, int flags) { char buf[128]; struct stat *st = &file->st; file->inode = st->st_ino; #ifdef S_BLKSIZE file->bsize = st->st_blocks * S_BLKSIZE / 1024; #else file->bsize = st->st_blocks * 512 / 1024; #endif if(S_ISDIR(st->st_mode)) file->mode[0] = 'd'; else if(S_ISCHR(st->st_mode)) file->mode[0] = 'c'; else if(S_ISBLK(st->st_mode)) file->mode[0] = 'b'; else if(S_ISREG(st->st_mode)) file->mode[0] = '-'; else if(S_ISFIFO(st->st_mode)) file->mode[0] = 'p'; else if(S_ISLNK(st->st_mode)) file->mode[0] = 'l'; else if(S_ISSOCK(st->st_mode)) file->mode[0] = 's'; #ifdef S_ISWHT else if(S_ISWHT(st->st_mode)) file->mode[0] = 'w'; #endif else file->mode[0] = '?'; { char *x[] = { "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx" }; strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]); strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]); strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]); if((st->st_mode & S_ISUID)) { if((st->st_mode & S_IXUSR)) file->mode[3] = 's'; else file->mode[3] = 'S'; } if((st->st_mode & S_ISGID)) { if((st->st_mode & S_IXGRP)) file->mode[6] = 's'; else file->mode[6] = 'S'; } if((st->st_mode & S_ISTXT)) { if((st->st_mode & S_IXOTH)) file->mode[9] = 't'; else file->mode[9] = 'T'; } } file->n_link = st->st_nlink; { struct passwd *pwd; pwd = getpwuid(st->st_uid); if(pwd == NULL) asprintf(&file->user, "%u", (unsigned)st->st_uid); else file->user = strdup(pwd->pw_name); } { struct group *grp; grp = getgrgid(st->st_gid); if(grp == NULL) asprintf(&file->group, "%u", (unsigned)st->st_gid); else file->group = strdup(grp->gr_name); } if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { #if defined(major) && defined(minor) asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)); asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)); #else /* Don't want to use the DDI/DKI crap. */ asprintf(&file->major, "%u", (unsigned)st->st_rdev); asprintf(&file->minor, "%u", 0); #endif } else asprintf(&file->size, "%lu", (unsigned long)st->st_size); { time_t t = time(NULL); struct tm *tm = localtime(&st->st_mtime); if((t - st->st_mtime > 6*30*24*60*60) || (st->st_mtime - t > 6*30*24*60*60)) strftime(buf, sizeof(buf), "%b %e %Y", tm); else strftime(buf, sizeof(buf), "%b %e %H:%M", tm); file->date = strdup(buf); } { const char *p = strrchr(filename, '/'); if(p) p++; else p = filename; file->filename = strdup(p); } if(S_ISLNK(st->st_mode)) { int n; n = readlink((char *)filename, buf, sizeof(buf)); if(n >= 0) { buf[n] = '\0'; file->link = strdup(buf); } else warn("%s: readlink", filename); } } static void print_file(FILE *out, int flags, struct fileinfo *f, int max_inode, int max_bsize, int max_n_link, int max_user, int max_group, int max_size, int max_major, int max_minor, int max_date) { if(f->filename == NULL) return; if(flags & LS_INODE) { sec_fprintf2(out, "%*d", max_inode, f->inode); sec_fprintf2(out, " "); } if(flags & LS_SIZE) { sec_fprintf2(out, "%*d", max_bsize, f->bsize); sec_fprintf2(out, " "); } sec_fprintf2(out, "%s", f->mode); sec_fprintf2(out, " "); sec_fprintf2(out, "%*d", max_n_link, f->n_link); sec_fprintf2(out, " "); sec_fprintf2(out, "%-*s", max_user, f->user); sec_fprintf2(out, " "); sec_fprintf2(out, "%-*s", max_group, f->group); sec_fprintf2(out, " "); if(f->major != NULL && f->minor != NULL) sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor); else sec_fprintf2(out, "%*s", max_size, f->size); sec_fprintf2(out, " "); sec_fprintf2(out, "%*s", max_date, f->date); sec_fprintf2(out, " "); sec_fprintf2(out, "%s", f->filename); if(f->link) sec_fprintf2(out, " -> %s", f->link); sec_fprintf2(out, "\r\n"); } static int compare_filename(struct fileinfo *a, struct fileinfo *b) { if(a->filename == NULL) return 1; if(b->filename == NULL) return -1; return strcmp(a->filename, b->filename); } static int compare_mtime(struct fileinfo *a, struct fileinfo *b) { if(a->filename == NULL) return 1; if(b->filename == NULL) return -1; return a->st.st_mtime - b->st.st_mtime; } static int compare_size(struct fileinfo *a, struct fileinfo *b) { if(a->filename == NULL) return 1; if(b->filename == NULL) return -1; return a->st.st_size - b->st.st_size; } static void list_dir(FILE *out, const char *directory, int flags); static int log10(int num) { int i = 1; while(num > 10) { i++; num /= 10; } return i; } /* * Operate as lstat but fake up entries for AFS mount points so we don't * have to fetch them. */ static int lstat_file (const char *file, struct stat *sb) { #ifdef KRB4 if (k_hasafs() && strcmp(file, ".") && strcmp(file, "..")) { struct ViceIoctl a_params; char *last; char *path_bkp; static ino_t ino_counter = 0, ino_last = 0; int ret; const int maxsize = 2048; path_bkp = strdup (file); if (path_bkp == NULL) return -1; a_params.out = malloc (maxsize); if (a_params.out == NULL) { free (path_bkp); return -1; } /* If path contains more than the filename alone - split it */ last = strrchr (path_bkp, '/'); if (last != NULL) { *last = '\0'; a_params.in = last + 1; } else a_params.in = (char *)file; a_params.in_size = strlen (a_params.in) + 1; a_params.out_size = maxsize; ret = k_pioctl (last ? path_bkp : "." , VIOC_AFS_STAT_MT_PT, &a_params, 0); free (a_params.out); if (ret < 0) { free (path_bkp); if (errno != EINVAL) return ret; else /* if we get EINVAL this is probably not a mountpoint */ return lstat (file, sb); } /* * wow this was a mountpoint, lets cook the struct stat * use . as a prototype */ ret = lstat (path_bkp, sb); free (path_bkp); if (ret < 0) return ret; if (ino_last == sb->st_ino) ino_counter++; else { ino_last = sb->st_ino; ino_counter = 0; } sb->st_ino += ino_counter; sb->st_nlink = 3; return 0; } #endif /* KRB4 */ return lstat (file, sb); } static void list_files(FILE *out, char **files, int n_files, int flags) { struct fileinfo *fi; int i; fi = calloc(n_files, sizeof(*fi)); if (fi == NULL) { sec_fprintf2(out, "ouf of memory\r\n"); return; } for(i = 0; i < n_files; i++) { if(lstat_file(files[i], &fi[i].st) < 0) { sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno)); fi[i].filename = NULL; } else { if((flags & LS_DIRS) == 0 && S_ISDIR(fi[i].st.st_mode)) { if(n_files > 1) sec_fprintf2(out, "%s:\r\n", files[i]); list_dir(out, files[i], flags); } else { make_fileinfo(files[i], &fi[i], flags); } } } switch(SORT_MODE(flags)) { case LS_SORT_NAME: qsort(fi, n_files, sizeof(*fi), (int (*)(const void*, const void*))compare_filename); break; case LS_SORT_MTIME: qsort(fi, n_files, sizeof(*fi), (int (*)(const void*, const void*))compare_mtime); break; case LS_SORT_SIZE: qsort(fi, n_files, sizeof(*fi), (int (*)(const void*, const void*))compare_size); break; } { int max_inode = 0; int max_bsize = 0; int max_n_link = 0; int max_user = 0; int max_group = 0; int max_size = 0; int max_major = 0; int max_minor = 0; int max_date = 0; for(i = 0; i < n_files; i++) { if(fi[i].filename == NULL) continue; if(fi[i].inode > max_inode) max_inode = fi[i].inode; if(fi[i].bsize > max_bsize) max_bsize = fi[i].bsize; if(fi[i].n_link > max_n_link) max_n_link = fi[i].n_link; if(strlen(fi[i].user) > max_user) max_user = strlen(fi[i].user); if(strlen(fi[i].group) > max_group) max_group = strlen(fi[i].group); if(fi[i].major != NULL && strlen(fi[i].major) > max_major) max_major = strlen(fi[i].major); if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor) max_minor = strlen(fi[i].minor); if(fi[i].size != NULL && strlen(fi[i].size) > max_size) max_size = strlen(fi[i].size); if(strlen(fi[i].date) > max_date) max_date = strlen(fi[i].date); } if(max_size < max_major + max_minor + 2) max_size = max_major + max_minor + 2; else if(max_size - max_minor - 2 > max_major) max_major = max_size - max_minor - 2; max_inode = log10(max_inode); max_bsize = log10(max_bsize); max_n_link = log10(max_n_link); if(flags & LS_SORT_REVERSE) for(i = n_files - 1; i >= 0; i--) print_file(out, flags, &fi[i], max_inode, max_bsize, max_n_link, max_user, max_group, max_size, max_major, max_minor, max_date); else for(i = 0; i < n_files; i++) print_file(out, flags, &fi[i], max_inode, max_bsize, max_n_link, max_user, max_group, max_size, max_major, max_minor, max_date); } } static void free_files (char **files, int n) { int i; for (i = 0; i < n; ++i) free (files[i]); free (files); } static void list_dir(FILE *out, const char *directory, int flags) { DIR *d = opendir(directory); struct dirent *ent; char **files = NULL; int n_files = 0; if(d == NULL) { sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno)); return; } while((ent = readdir(d)) != NULL) { void *tmp; if(ent->d_name[0] == '.') { if (flags & LS_IGNORE_DOT) continue; if (ent->d_name[1] == 0) /* Ignore . */ continue; if (ent->d_name[1] == '.' && ent->d_name[2] == 0) /* Ignore .. */ continue; } tmp = realloc(files, (n_files + 1) * sizeof(*files)); if (tmp == NULL) { sec_fprintf2(out, "%s: out of memory\r\n", directory); free_files (files, n_files); closedir (d); return; } files = tmp; asprintf(&files[n_files], "%s/%s", directory, ent->d_name); if (files[n_files] == NULL) { sec_fprintf2(out, "%s: out of memory\r\n", directory); free_files (files, n_files); closedir (d); return; } ++n_files; } closedir(d); list_files(out, files, n_files, flags | LS_DIRS); } void builtin_ls(FILE *out, const char *file) { int flags = LS_SORT_NAME; if(*file == '-') { const char *p; for(p = file + 1; *p; p++) { switch(*p) { case 'a': case 'A': flags &= ~LS_IGNORE_DOT; break; case 'C': break; case 'd': flags |= LS_DIRS; break; case 'f': flags = (flags & ~LS_SORT_MODE); break; case 'i': flags |= flags | LS_INODE; break; case 'l': break; case 't': flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME; break; case 's': flags |= LS_SIZE; break; case 'S': flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE; break; case 'r': flags |= LS_SORT_REVERSE; break; } } file = "."; } list_files(out, &file, 1, flags); sec_fflush(out); }