1 /*
2  * (C) Copyright 2002
3  * St�ubli Faverges - <www.staubli.com>
4  * Pierre AUBERT  p.aubert@staubli.com
5  *
6  * See file CREDITS for list of people who contributed to this
7  * project.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  * MA 02111-1307 USA
23  */
24 
25 #include <common.h>
26 #include <config.h>
27 #include <linux/ctype.h>
28 
29 #include "dos.h"
30 #include "fdos.h"
31 
32 static int dir_read (Fs_t *fs,
33 		     Slot_t *dir,
34 		     Directory_t *dirent,
35 		     int num,
36 		     struct vfat_state *v);
37 
38 static int unicode_read (char *in, char *out, int num);
39 static int match (const char *s, const char *p);
40 static unsigned char sum_shortname (char *name);
41 static int check_vfat (struct vfat_state *v, Directory_t *dir);
42 static char *conv_name (char *name, char *ext, char Case, char *ans);
43 
44 
45 /*-----------------------------------------------------------------------------
46  * clear_vfat --
47  *-----------------------------------------------------------------------------
48  */
clear_vfat(struct vfat_state * v)49 static void clear_vfat (struct vfat_state *v)
50 {
51     v -> subentries = 0;
52     v -> status = 0;
53 }
54 
55 /*-----------------------------------------------------------------------------
56  * vfat_lookup --
57  *-----------------------------------------------------------------------------
58  */
vfat_lookup(Slot_t * dir,Fs_t * fs,Directory_t * dirent,int * entry,int * vfat_start,char * filename,int flags,char * outname,Slot_t * file)59 int vfat_lookup (Slot_t *dir,
60 		 Fs_t *fs,
61 		 Directory_t *dirent,
62 		 int *entry,
63 		 int *vfat_start,
64 		 char *filename,
65 		 int flags,
66 		 char *outname,
67 		 Slot_t *file)
68 {
69     int found;
70     struct vfat_state vfat;
71     char newfile [VSE_NAMELEN];
72     int vfat_present = 0;
73 
74     if (*entry == -1) {
75 	return -1;
76     }
77 
78     found = 0;
79     clear_vfat (&vfat);
80     while (1) {
81 	if (dir_read (fs, dir, dirent, *entry, &vfat) < 0) {
82 	    if (vfat_start) {
83 		*vfat_start = *entry;
84 	    }
85 	    break;
86 	}
87 	(*entry)++;
88 
89 	/* Empty slot                                                        */
90 	if (dirent -> name[0] == '\0'){
91 	    if (vfat_start == 0) {
92 		break;
93 	    }
94 	    continue;
95 	}
96 
97 	if (dirent -> attr == ATTR_VSE) {
98 	    /* VSE entry, continue                                           */
99 	    continue;
100 	}
101 	if ( (dirent -> name [0] == DELMARK) ||
102 	     ((dirent -> attr & ATTR_DIRECTORY) != 0 &&
103 	      (flags & ACCEPT_DIR) == 0) ||
104 	     ((dirent -> attr & ATTR_VOLUME) != 0 &&
105 	      (flags & ACCEPT_LABEL) == 0) ||
106 	     (((dirent -> attr & (ATTR_DIRECTORY | ATTR_VOLUME)) == 0) &&
107 	      (flags & ACCEPT_PLAIN) == 0)) {
108 	    clear_vfat (&vfat);
109 	    continue;
110 	}
111 
112 	vfat_present = check_vfat (&vfat, dirent);
113 	if (vfat_start) {
114 	    *vfat_start = *entry - 1;
115 	    if (vfat_present) {
116 		*vfat_start -= vfat.subentries;
117 	    }
118 	}
119 
120 	if (dirent -> attr & ATTR_VOLUME) {
121 	    strncpy (newfile, dirent -> name, 8);
122 	    newfile [8] = '\0';
123 	    strncat (newfile, dirent -> ext, 3);
124 	    newfile [11] = '\0';
125 	}
126 	else {
127 	    conv_name (dirent -> name, dirent -> ext, dirent -> Case, newfile);
128 	}
129 
130 	if (flags & MATCH_ANY) {
131 	    found = 1;
132 	    break;
133 	}
134 
135 	if ((vfat_present && match (vfat.name, filename)) ||
136 	    (match (newfile, filename))) {
137 	    found = 1;
138 	    break;
139 	}
140 	clear_vfat (&vfat);
141     }
142 
143     if (found) {
144 	if ((flags & DO_OPEN) && file) {
145 	    if (open_file (file, dirent) < 0) {
146 		return (-1);
147 	    }
148 	}
149 	if (outname) {
150 	    if (vfat_present) {
151 		strcpy (outname, vfat.name);
152 	    }
153 	    else {
154 		strcpy (outname, newfile);
155 	    }
156 	}
157 	return (0);                    /* File found                         */
158     } else {
159 	*entry = -1;
160 	return -1;                      /* File not found                    */
161     }
162 }
163 
164 /*-----------------------------------------------------------------------------
165  * dir_read -- Read one directory entry
166  *-----------------------------------------------------------------------------
167  */
dir_read(Fs_t * fs,Slot_t * dir,Directory_t * dirent,int num,struct vfat_state * v)168 static int dir_read (Fs_t *fs,
169 	      Slot_t *dir,
170 	      Directory_t *dirent,
171 	      int num,
172 	      struct vfat_state *v)
173 {
174 
175     /* read the directory entry                                              */
176     if (read_file (fs,
177 		   dir,
178 		   (char *)dirent,
179 		   num * MDIR_SIZE,
180 		   MDIR_SIZE) != MDIR_SIZE) {
181 	return (-1);
182     }
183 
184     if (v && (dirent -> attr == ATTR_VSE)) {
185 	struct vfat_subentry *vse;
186 	unsigned char id, last_flag;
187 	char *c;
188 
189 	vse = (struct vfat_subentry *) dirent;
190 	id = vse -> id & VSE_MASK;
191 	last_flag = (vse -> id & VSE_LAST);
192 	if (id > MAX_VFAT_SUBENTRIES) {
193 	    /* Invalid VSE entry                                             */
194 	    return (-1);
195 	}
196 
197 
198 	/* Decode VSE                                                        */
199 	if(v -> sum != vse -> sum) {
200 	    clear_vfat (v);
201 	    v -> sum = vse -> sum;
202 	}
203 
204 
205 	v -> status |= 1 << (id - 1);
206 	if (last_flag) {
207 	    v -> subentries = id;
208 	}
209 
210 	c = &(v -> name [VSE_NAMELEN * (id - 1)]);
211 	c += unicode_read (vse->text1, c, VSE1SIZE);
212 	c += unicode_read (vse->text2, c, VSE2SIZE);
213 	c += unicode_read (vse->text3, c, VSE3SIZE);
214 
215 	if (last_flag) {
216 	    *c = '\0';	        /* Null terminate long name                  */
217 	}
218 
219     }
220     return (0);
221 }
222 
223 /*-----------------------------------------------------------------------------
224  * unicode_read --
225  *-----------------------------------------------------------------------------
226  */
unicode_read(char * in,char * out,int num)227 static int unicode_read (char *in, char *out, int num)
228 {
229     int j;
230 
231     for (j = 0; j < num; ++j) {
232 	if (in [1])
233 	    *out = '_';
234 	else
235 	    *out = in [0];
236 	out ++;
237 	in += 2;
238     }
239     return num;
240 }
241 
242 /*-----------------------------------------------------------------------------
243  * match --
244  *-----------------------------------------------------------------------------
245  */
match(const char * s,const char * p)246 static int match (const char *s, const char *p)
247 {
248 
249     for (; *p != '\0'; ) {
250 	if (toupper (*s) != toupper (*p)) {
251 	    return (0);
252 	}
253 	p++;
254 	s++;
255     }
256 
257     if (*s != '\0') {
258 	return (0);
259     }
260     else {
261 	return (1);
262     }
263 }
264 /*-----------------------------------------------------------------------------
265  * sum_shortname --
266  *-----------------------------------------------------------------------------
267  */
sum_shortname(char * name)268 static unsigned char sum_shortname (char *name)
269 {
270     unsigned char sum;
271     int j;
272 
273     for (j = sum = 0; j < 11; ++j) {
274 	sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) +
275 	    (name [j] ? name [j] : ' ');
276     }
277     return (sum);
278 }
279 /*-----------------------------------------------------------------------------
280  * check_vfat --
281  * Return 1 if long name is valid, 0 else
282  *-----------------------------------------------------------------------------
283  */
check_vfat(struct vfat_state * v,Directory_t * dir)284 static int check_vfat (struct vfat_state *v, Directory_t *dir)
285 {
286     char name[12];
287 
288     if (v -> subentries == 0) {
289 	return 0;
290     }
291 
292     strncpy (name, dir -> name, 8);
293     strncpy (name + 8, dir -> ext, 3);
294     name [11] = '\0';
295 
296     if (v -> sum != sum_shortname (name)) {
297 	return 0;
298     }
299 
300     if( (v -> status & ((1 << v -> subentries) - 1)) !=
301 	(1 << v -> subentries) - 1) {
302 	return 0;
303     }
304     v->name [VSE_NAMELEN * v -> subentries] = 0;
305 
306     return 1;
307 }
308 /*-----------------------------------------------------------------------------
309  * conv_name --
310  *-----------------------------------------------------------------------------
311  */
conv_name(char * name,char * ext,char Case,char * ans)312 static char *conv_name (char *name, char *ext, char Case, char *ans)
313 {
314     char tname [9], text [4];
315     int i;
316 
317     i = 0;
318     while (i < 8 && name [i] != ' ' && name [i] != '\0') {
319 	tname [i] = name [i];
320 	i++;
321     }
322     tname [i] = '\0';
323 
324     if (Case & BASECASE) {
325 	for (i = 0; i < 8 && tname [i]; i++) {
326 	    tname [i] = tolower (tname [i]);
327 	}
328     }
329 
330     i = 0;
331     while (i < 3 && ext [i] != ' ' && ext [i] != '\0') {
332 	text [i] = ext [i];
333 	i++;
334     }
335     text [i] = '\0';
336 
337     if (Case & EXTCASE){
338 	for (i = 0; i < 3 && text [i]; i++) {
339 	    text [i] = tolower (text [i]);
340 	}
341     }
342 
343     if (*text) {
344 	strcpy (ans, tname);
345 	strcat (ans, ".");
346 	strcat (ans, text);
347     }
348     else {
349 	strcpy(ans, tname);
350     }
351     return (ans);
352 }
353