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
dirtype(char type)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
ftp_readdir(struct inode * inode,struct dirent * dirent)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