blob: 2b87d0f5a740990f68ec9bb476a59946d5dbb38e [file] [log] [blame]
wdenk591dda52002-11-18 00:14:45 +00001/*
2 * (C) Copyright 2002
Albert ARIBAUD60fbc8d2011-08-04 18:45:45 +02003 * Stäubli Faverges - <www.staubli.com>
wdenk591dda52002-11-18 00:14:45 +00004 * Pierre AUBERT p.aubert@staubli.com
5 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02006 * SPDX-License-Identifier: GPL-2.0+
wdenk591dda52002-11-18 00:14:45 +00007 */
8
9#include <common.h>
10#include <config.h>
wdenk591dda52002-11-18 00:14:45 +000011#include <linux/ctype.h>
12
13#include "dos.h"
14#include "fdos.h"
15
16static int dir_read (Fs_t *fs,
wdenk57b2d802003-06-27 21:31:46 +000017 Slot_t *dir,
18 Directory_t *dirent,
19 int num,
20 struct vfat_state *v);
wdenk591dda52002-11-18 00:14:45 +000021
22static int unicode_read (char *in, char *out, int num);
23static int match (const char *s, const char *p);
24static unsigned char sum_shortname (char *name);
25static int check_vfat (struct vfat_state *v, Directory_t *dir);
26static char *conv_name (char *name, char *ext, char Case, char *ans);
27
28
29/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000030 * clear_vfat --
wdenk591dda52002-11-18 00:14:45 +000031 *-----------------------------------------------------------------------------
32 */
33static void clear_vfat (struct vfat_state *v)
34{
35 v -> subentries = 0;
36 v -> status = 0;
37}
38
39/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000040 * vfat_lookup --
wdenk591dda52002-11-18 00:14:45 +000041 *-----------------------------------------------------------------------------
42 */
43int vfat_lookup (Slot_t *dir,
wdenk57b2d802003-06-27 21:31:46 +000044 Fs_t *fs,
45 Directory_t *dirent,
46 int *entry,
47 int *vfat_start,
48 char *filename,
49 int flags,
50 char *outname,
51 Slot_t *file)
wdenk591dda52002-11-18 00:14:45 +000052{
53 int found;
54 struct vfat_state vfat;
55 char newfile [VSE_NAMELEN];
56 int vfat_present = 0;
57
58 if (*entry == -1) {
wdenk57b2d802003-06-27 21:31:46 +000059 return -1;
wdenk591dda52002-11-18 00:14:45 +000060 }
61
62 found = 0;
63 clear_vfat (&vfat);
64 while (1) {
wdenk57b2d802003-06-27 21:31:46 +000065 if (dir_read (fs, dir, dirent, *entry, &vfat) < 0) {
66 if (vfat_start) {
67 *vfat_start = *entry;
68 }
69 break;
70 }
71 (*entry)++;
wdenk591dda52002-11-18 00:14:45 +000072
wdenk57b2d802003-06-27 21:31:46 +000073 /* Empty slot */
74 if (dirent -> name[0] == '\0'){
75 if (vfat_start == 0) {
76 break;
77 }
78 continue;
79 }
wdenk591dda52002-11-18 00:14:45 +000080
wdenk57b2d802003-06-27 21:31:46 +000081 if (dirent -> attr == ATTR_VSE) {
82 /* VSE entry, continue */
83 continue;
84 }
85 if ( (dirent -> name [0] == DELMARK) ||
86 ((dirent -> attr & ATTR_DIRECTORY) != 0 &&
87 (flags & ACCEPT_DIR) == 0) ||
88 ((dirent -> attr & ATTR_VOLUME) != 0 &&
89 (flags & ACCEPT_LABEL) == 0) ||
90 (((dirent -> attr & (ATTR_DIRECTORY | ATTR_VOLUME)) == 0) &&
91 (flags & ACCEPT_PLAIN) == 0)) {
92 clear_vfat (&vfat);
93 continue;
94 }
wdenk591dda52002-11-18 00:14:45 +000095
wdenk57b2d802003-06-27 21:31:46 +000096 vfat_present = check_vfat (&vfat, dirent);
97 if (vfat_start) {
98 *vfat_start = *entry - 1;
99 if (vfat_present) {
100 *vfat_start -= vfat.subentries;
101 }
102 }
wdenk591dda52002-11-18 00:14:45 +0000103
wdenk57b2d802003-06-27 21:31:46 +0000104 if (dirent -> attr & ATTR_VOLUME) {
105 strncpy (newfile, dirent -> name, 8);
106 newfile [8] = '\0';
107 strncat (newfile, dirent -> ext, 3);
108 newfile [11] = '\0';
109 }
110 else {
111 conv_name (dirent -> name, dirent -> ext, dirent -> Case, newfile);
112 }
wdenk591dda52002-11-18 00:14:45 +0000113
wdenk57b2d802003-06-27 21:31:46 +0000114 if (flags & MATCH_ANY) {
115 found = 1;
116 break;
117 }
wdenk591dda52002-11-18 00:14:45 +0000118
wdenk57b2d802003-06-27 21:31:46 +0000119 if ((vfat_present && match (vfat.name, filename)) ||
120 (match (newfile, filename))) {
121 found = 1;
122 break;
123 }
124 clear_vfat (&vfat);
wdenk591dda52002-11-18 00:14:45 +0000125 }
126
127 if (found) {
wdenk57b2d802003-06-27 21:31:46 +0000128 if ((flags & DO_OPEN) && file) {
129 if (open_file (file, dirent) < 0) {
130 return (-1);
131 }
132 }
133 if (outname) {
134 if (vfat_present) {
135 strcpy (outname, vfat.name);
136 }
137 else {
138 strcpy (outname, newfile);
139 }
140 }
141 return (0); /* File found */
wdenk591dda52002-11-18 00:14:45 +0000142 } else {
wdenk57b2d802003-06-27 21:31:46 +0000143 *entry = -1;
144 return -1; /* File not found */
wdenk591dda52002-11-18 00:14:45 +0000145 }
146}
147
148/*-----------------------------------------------------------------------------
149 * dir_read -- Read one directory entry
150 *-----------------------------------------------------------------------------
151 */
152static int dir_read (Fs_t *fs,
wdenk57b2d802003-06-27 21:31:46 +0000153 Slot_t *dir,
154 Directory_t *dirent,
155 int num,
156 struct vfat_state *v)
wdenk591dda52002-11-18 00:14:45 +0000157{
158
159 /* read the directory entry */
160 if (read_file (fs,
wdenk57b2d802003-06-27 21:31:46 +0000161 dir,
162 (char *)dirent,
163 num * MDIR_SIZE,
164 MDIR_SIZE) != MDIR_SIZE) {
165 return (-1);
wdenk591dda52002-11-18 00:14:45 +0000166 }
167
168 if (v && (dirent -> attr == ATTR_VSE)) {
wdenk57b2d802003-06-27 21:31:46 +0000169 struct vfat_subentry *vse;
170 unsigned char id, last_flag;
171 char *c;
wdenk591dda52002-11-18 00:14:45 +0000172
wdenk57b2d802003-06-27 21:31:46 +0000173 vse = (struct vfat_subentry *) dirent;
174 id = vse -> id & VSE_MASK;
175 last_flag = (vse -> id & VSE_LAST);
176 if (id > MAX_VFAT_SUBENTRIES) {
177 /* Invalid VSE entry */
178 return (-1);
179 }
wdenk591dda52002-11-18 00:14:45 +0000180
181
wdenk57b2d802003-06-27 21:31:46 +0000182 /* Decode VSE */
183 if(v -> sum != vse -> sum) {
184 clear_vfat (v);
185 v -> sum = vse -> sum;
186 }
wdenk591dda52002-11-18 00:14:45 +0000187
wdenk591dda52002-11-18 00:14:45 +0000188
wdenk57b2d802003-06-27 21:31:46 +0000189 v -> status |= 1 << (id - 1);
190 if (last_flag) {
191 v -> subentries = id;
192 }
193
194 c = &(v -> name [VSE_NAMELEN * (id - 1)]);
195 c += unicode_read (vse->text1, c, VSE1SIZE);
196 c += unicode_read (vse->text2, c, VSE2SIZE);
197 c += unicode_read (vse->text3, c, VSE3SIZE);
198
199 if (last_flag) {
200 *c = '\0'; /* Null terminate long name */
201 }
202
wdenk591dda52002-11-18 00:14:45 +0000203 }
204 return (0);
205}
206
207/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000208 * unicode_read --
wdenk591dda52002-11-18 00:14:45 +0000209 *-----------------------------------------------------------------------------
210 */
211static int unicode_read (char *in, char *out, int num)
212{
213 int j;
wdenk57b2d802003-06-27 21:31:46 +0000214
wdenk591dda52002-11-18 00:14:45 +0000215 for (j = 0; j < num; ++j) {
wdenk57b2d802003-06-27 21:31:46 +0000216 if (in [1])
217 *out = '_';
218 else
219 *out = in [0];
220 out ++;
221 in += 2;
wdenk591dda52002-11-18 00:14:45 +0000222 }
223 return num;
224}
225
226/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000227 * match --
wdenk591dda52002-11-18 00:14:45 +0000228 *-----------------------------------------------------------------------------
229 */
230static int match (const char *s, const char *p)
231{
232
233 for (; *p != '\0'; ) {
wdenk57b2d802003-06-27 21:31:46 +0000234 if (toupper (*s) != toupper (*p)) {
235 return (0);
236 }
237 p++;
238 s++;
wdenk591dda52002-11-18 00:14:45 +0000239 }
wdenk57b2d802003-06-27 21:31:46 +0000240
wdenk591dda52002-11-18 00:14:45 +0000241 if (*s != '\0') {
wdenk57b2d802003-06-27 21:31:46 +0000242 return (0);
wdenk591dda52002-11-18 00:14:45 +0000243 }
244 else {
wdenk57b2d802003-06-27 21:31:46 +0000245 return (1);
wdenk591dda52002-11-18 00:14:45 +0000246 }
247}
248/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000249 * sum_shortname --
wdenk591dda52002-11-18 00:14:45 +0000250 *-----------------------------------------------------------------------------
251 */
252static unsigned char sum_shortname (char *name)
253{
254 unsigned char sum;
255 int j;
wdenk57b2d802003-06-27 21:31:46 +0000256
wdenk591dda52002-11-18 00:14:45 +0000257 for (j = sum = 0; j < 11; ++j) {
wdenk57b2d802003-06-27 21:31:46 +0000258 sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) +
259 (name [j] ? name [j] : ' ');
wdenk591dda52002-11-18 00:14:45 +0000260 }
261 return (sum);
262}
263/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000264 * check_vfat --
wdenk591dda52002-11-18 00:14:45 +0000265 * Return 1 if long name is valid, 0 else
266 *-----------------------------------------------------------------------------
267 */
268static int check_vfat (struct vfat_state *v, Directory_t *dir)
269{
270 char name[12];
wdenk57b2d802003-06-27 21:31:46 +0000271
wdenk591dda52002-11-18 00:14:45 +0000272 if (v -> subentries == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000273 return 0;
wdenk591dda52002-11-18 00:14:45 +0000274 }
wdenk57b2d802003-06-27 21:31:46 +0000275
wdenk591dda52002-11-18 00:14:45 +0000276 strncpy (name, dir -> name, 8);
277 strncpy (name + 8, dir -> ext, 3);
278 name [11] = '\0';
wdenk57b2d802003-06-27 21:31:46 +0000279
wdenk591dda52002-11-18 00:14:45 +0000280 if (v -> sum != sum_shortname (name)) {
wdenk57b2d802003-06-27 21:31:46 +0000281 return 0;
wdenk591dda52002-11-18 00:14:45 +0000282 }
wdenk57b2d802003-06-27 21:31:46 +0000283
wdenk591dda52002-11-18 00:14:45 +0000284 if( (v -> status & ((1 << v -> subentries) - 1)) !=
wdenk57b2d802003-06-27 21:31:46 +0000285 (1 << v -> subentries) - 1) {
286 return 0;
wdenk591dda52002-11-18 00:14:45 +0000287 }
288 v->name [VSE_NAMELEN * v -> subentries] = 0;
wdenk57b2d802003-06-27 21:31:46 +0000289
wdenk591dda52002-11-18 00:14:45 +0000290 return 1;
291}
292/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000293 * conv_name --
wdenk591dda52002-11-18 00:14:45 +0000294 *-----------------------------------------------------------------------------
295 */
296static char *conv_name (char *name, char *ext, char Case, char *ans)
297{
298 char tname [9], text [4];
299 int i;
300
301 i = 0;
302 while (i < 8 && name [i] != ' ' && name [i] != '\0') {
wdenk57b2d802003-06-27 21:31:46 +0000303 tname [i] = name [i];
304 i++;
wdenk591dda52002-11-18 00:14:45 +0000305 }
306 tname [i] = '\0';
wdenk57b2d802003-06-27 21:31:46 +0000307
wdenk591dda52002-11-18 00:14:45 +0000308 if (Case & BASECASE) {
wdenk57b2d802003-06-27 21:31:46 +0000309 for (i = 0; i < 8 && tname [i]; i++) {
310 tname [i] = tolower (tname [i]);
311 }
wdenk591dda52002-11-18 00:14:45 +0000312 }
313
314 i = 0;
315 while (i < 3 && ext [i] != ' ' && ext [i] != '\0') {
wdenk57b2d802003-06-27 21:31:46 +0000316 text [i] = ext [i];
317 i++;
wdenk591dda52002-11-18 00:14:45 +0000318 }
319 text [i] = '\0';
320
321 if (Case & EXTCASE){
wdenk57b2d802003-06-27 21:31:46 +0000322 for (i = 0; i < 3 && text [i]; i++) {
323 text [i] = tolower (text [i]);
324 }
wdenk591dda52002-11-18 00:14:45 +0000325 }
326
327 if (*text) {
wdenk57b2d802003-06-27 21:31:46 +0000328 strcpy (ans, tname);
329 strcat (ans, ".");
330 strcat (ans, text);
wdenk591dda52002-11-18 00:14:45 +0000331 }
332 else {
wdenk57b2d802003-06-27 21:31:46 +0000333 strcpy(ans, tname);
wdenk591dda52002-11-18 00:14:45 +0000334 }
335 return (ans);
336}