1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2011 Intel Corporation; author: H. Peter Anvin 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 8 * Boston MA 02110-1301, USA; either version 2 of the License, or 9 * (at your option) any later version; incorporated herein by reference. 10 * 11 * ----------------------------------------------------------------------- */ 12 13 /* 14 * ftp_readdir.c 15 */ 16 #include <inttypes.h> 17 #include <string.h> 18 #include <stdlib.h> 19 #include <ctype.h> 20 #include <dprintf.h> 21 #include "pxe.h" 22 23 static int dirtype(char type) 24 { 25 switch (type) { 26 case 'f': 27 return DT_FIFO; 28 case 'c': 29 return DT_CHR; 30 case 'd': 31 return DT_DIR; 32 case 'b': 33 return DT_BLK; 34 case '-': 35 case '0' ... '9': /* Some DOS FTP stacks */ 36 return DT_REG; 37 case 'l': 38 return DT_LNK; 39 case 's': 40 return DT_SOCK; 41 default: 42 return DT_UNKNOWN; 43 } 44 } 45 46 int ftp_readdir(struct inode *inode, struct dirent *dirent) 47 { 48 char bufs[2][FILENAME_MAX + 1]; 49 int nbuf = 0; 50 char *buf = bufs[nbuf]; 51 char *p = buf; 52 char *name = NULL; 53 char type; 54 int c; 55 int dt; 56 bool was_cr = false; 57 bool first = true; 58 59 for (;;) { 60 type = 0; 61 62 for (;;) { 63 c = pxe_getc(inode); 64 if (c == -1) 65 return -1; /* Nothing else there */ 66 67 if (c == '\r') { 68 was_cr = true; 69 continue; 70 } 71 if (was_cr) { 72 if (c == '\n') { 73 if (!name) { 74 *p = '\0'; 75 name = buf; 76 } 77 break; /* End of line */ 78 } 79 else if (c == '\0') 80 c = '\r'; 81 } 82 was_cr = false; 83 84 if (c == ' ' || c == '\t') { 85 if (!name) { 86 *p = '\0'; 87 if (first) { 88 if (p == buf) { 89 /* Name started with whitespace - skip line */ 90 name = buf; 91 } else if ((p = strchr(buf, ';'))) { 92 /* VMS/Multinet format */ 93 if (p > buf+4 && !memcmp(p-4, ".DIR", 4)) { 94 type = 'd'; 95 p -= 4; 96 } else { 97 type = 'f'; 98 } 99 *p = '\0'; 100 name = buf; 101 } else { 102 type = buf[0]; 103 } 104 first = false; 105 } else { 106 /* Not the first word */ 107 if ((type >= '0' && type <= '9') && 108 !strcmp(buf, "<DIR>")) { 109 /* Some DOS FTP servers */ 110 type = 'd'; 111 } else if (type == 'l' && !strcmp(buf, "->")) { 112 /* The name was the previous word */ 113 name = bufs[nbuf ^ 1]; 114 } 115 } 116 nbuf ^= 1; 117 p = buf = bufs[nbuf]; 118 } 119 } else { 120 if (!name && p < buf + FILENAME_MAX) 121 *p++ = c; 122 } 123 } 124 125 dt = dirtype(type); 126 if (dt != DT_UNKNOWN) { 127 size_t len = strlen(name); 128 129 if (len <= NAME_MAX) { 130 dirent->d_type = dt; 131 dirent->d_ino = 0; /* Not applicable */ 132 dirent->d_off = 0; /* Not applicable */ 133 dirent->d_reclen = offsetof(struct dirent, d_name) + len+1; 134 memcpy(dirent->d_name, name, len+1); 135 return 0; 136 } 137 } 138 139 /* Otherwise try the next line... */ 140 } 141 } 142