blob: 97f6fb710202d981ecfb3eccf646cc45479d77f3 [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>
11#include <malloc.h>
12
wdenk591dda52002-11-18 00:14:45 +000013#include "dos.h"
14#include "fdos.h"
15
16static int cache_sect;
17static unsigned char cache [SZ_STD_SECTOR];
18
19
20#define min(x,y) ((x)<(y)?(x):(y))
21
22static int descend (Slot_t *parent,
wdenk57b2d802003-06-27 21:31:46 +000023 Fs_t *fs,
24 char *path);
wdenk591dda52002-11-18 00:14:45 +000025
26/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000027 * init_subdir --
wdenk591dda52002-11-18 00:14:45 +000028 *-----------------------------------------------------------------------------
29 */
30void init_subdir (void)
31{
32 cache_sect = -1;
33}
34/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000035 * basename --
wdenk591dda52002-11-18 00:14:45 +000036 *-----------------------------------------------------------------------------
37 */
38char *basename (char *name)
39{
40 register char *cptr;
41
42 if (!name || !*name) {
wdenk57b2d802003-06-27 21:31:46 +000043 return ("");
wdenk591dda52002-11-18 00:14:45 +000044 }
wdenk57b2d802003-06-27 21:31:46 +000045
wdenk591dda52002-11-18 00:14:45 +000046 for (cptr= name; *cptr++; );
47 while (--cptr >= name) {
48 if (*cptr == '/') {
wdenk57b2d802003-06-27 21:31:46 +000049 return (cptr + 1);
wdenk591dda52002-11-18 00:14:45 +000050 }
51 }
52 return(name);
53}
54/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000055 * root_map --
wdenk591dda52002-11-18 00:14:45 +000056 *-----------------------------------------------------------------------------
57 */
58static int root_map (Fs_t *fs, Slot_t *file, int where, int *len)
59{
60 *len = min (*len, fs -> dir_len * SZ_STD_SECTOR - where);
61 if (*len < 0 ) {
wdenk57b2d802003-06-27 21:31:46 +000062 *len = 0;
63 return (-1);
wdenk591dda52002-11-18 00:14:45 +000064 }
65 return fs -> dir_start * SZ_STD_SECTOR + where;
66}
67/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000068 * normal_map --
wdenk591dda52002-11-18 00:14:45 +000069 *-----------------------------------------------------------------------------
70 */
71static int normal_map (Fs_t *fs, Slot_t *file, int where, int *len)
72{
73 int offset;
74 int NrClu;
75 unsigned short RelCluNr;
76 unsigned short CurCluNr;
77 unsigned short NewCluNr;
78 unsigned short AbsCluNr;
79 int clus_size;
80
81 clus_size = fs -> cluster_size * SZ_STD_SECTOR;
82 offset = where % clus_size;
83
84 *len = min (*len, file -> FileSize - where);
85
86 if (*len < 0 ) {
wdenk57b2d802003-06-27 21:31:46 +000087 *len = 0;
88 return (0);
wdenk591dda52002-11-18 00:14:45 +000089 }
90
91 if (file -> FirstAbsCluNr < 2){
wdenk57b2d802003-06-27 21:31:46 +000092 *len = 0;
93 return (0);
wdenk591dda52002-11-18 00:14:45 +000094 }
95
96 RelCluNr = where / clus_size;
wdenk57b2d802003-06-27 21:31:46 +000097
wdenk591dda52002-11-18 00:14:45 +000098 if (RelCluNr >= file -> PreviousRelCluNr){
wdenk57b2d802003-06-27 21:31:46 +000099 CurCluNr = file -> PreviousRelCluNr;
100 AbsCluNr = file -> PreviousAbsCluNr;
wdenk591dda52002-11-18 00:14:45 +0000101 } else {
wdenk57b2d802003-06-27 21:31:46 +0000102 CurCluNr = 0;
103 AbsCluNr = file -> FirstAbsCluNr;
wdenk591dda52002-11-18 00:14:45 +0000104 }
105
106
107 NrClu = (offset + *len - 1) / clus_size;
108 while (CurCluNr <= RelCluNr + NrClu) {
wdenk57b2d802003-06-27 21:31:46 +0000109 if (CurCluNr == RelCluNr){
110 /* we have reached the beginning of our zone. Save
111 * coordinates */
112 file -> PreviousRelCluNr = RelCluNr;
113 file -> PreviousAbsCluNr = AbsCluNr;
114 }
115 NewCluNr = fat_decode (fs, AbsCluNr);
116 if (NewCluNr == 1 || NewCluNr == 0) {
117 PRINTF("Fat problem while decoding %d %x\n",
118 AbsCluNr, NewCluNr);
119 return (-1);
120 }
121 if (CurCluNr == RelCluNr + NrClu) {
122 break;
123 }
wdenk591dda52002-11-18 00:14:45 +0000124
wdenk57b2d802003-06-27 21:31:46 +0000125 if (CurCluNr < RelCluNr && NewCluNr == FAT12_END) {
126 *len = 0;
127 return 0;
128 }
wdenk591dda52002-11-18 00:14:45 +0000129
wdenk57b2d802003-06-27 21:31:46 +0000130 if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1)
131 break;
132 CurCluNr++;
133 AbsCluNr = NewCluNr;
wdenk591dda52002-11-18 00:14:45 +0000134 }
135
136 *len = min (*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
137
138 return (((file -> PreviousAbsCluNr - 2) * fs -> cluster_size +
wdenk57b2d802003-06-27 21:31:46 +0000139 fs -> dir_start + fs -> dir_len) *
140 SZ_STD_SECTOR + offset);
wdenk591dda52002-11-18 00:14:45 +0000141}
142/*-----------------------------------------------------------------------------
143 * open_subdir -- open the subdir containing the file
144 *-----------------------------------------------------------------------------
145 */
146int open_subdir (File_t *desc)
147{
148 char *pathname;
149 char *tmp, *s, *path;
150 char terminator;
wdenk57b2d802003-06-27 21:31:46 +0000151
wdenk591dda52002-11-18 00:14:45 +0000152 if ((pathname = (char *)malloc (MAX_PATH)) == NULL) {
wdenk57b2d802003-06-27 21:31:46 +0000153 return (-1);
wdenk591dda52002-11-18 00:14:45 +0000154 }
wdenk57b2d802003-06-27 21:31:46 +0000155
wdenk591dda52002-11-18 00:14:45 +0000156 strcpy (pathname, desc -> name);
wdenk57b2d802003-06-27 21:31:46 +0000157
wdenk591dda52002-11-18 00:14:45 +0000158 /* Suppress file name */
159 tmp = basename (pathname);
160 *tmp = '\0';
161
162 /* root directory init */
163 desc -> subdir.FirstAbsCluNr = 0;
164 desc -> subdir.FileSize = -1;
165 desc -> subdir.map = root_map;
166 desc -> subdir.dir.attr = ATTR_DIRECTORY;
wdenk57b2d802003-06-27 21:31:46 +0000167
wdenk591dda52002-11-18 00:14:45 +0000168 tmp = pathname;
169 for (s = tmp; ; ++s) {
wdenk57b2d802003-06-27 21:31:46 +0000170 if (*s == '/' || *s == '\0') {
171 path = tmp;
172 terminator = *s;
173 *s = '\0';
174 if (s != tmp && strcmp (path,".")) {
175 if (descend (&desc -> subdir, desc -> fs, path) < 0) {
176 free (pathname);
177 return (-1);
178 }
179 }
180 if (terminator == 0) {
181 break;
182 }
183 tmp = s + 1;
184 }
wdenk591dda52002-11-18 00:14:45 +0000185 }
186 free (pathname);
187 return (0);
188}
189/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000190 * descend --
wdenk591dda52002-11-18 00:14:45 +0000191 *-----------------------------------------------------------------------------
192 */
193static int descend (Slot_t *parent,
wdenk57b2d802003-06-27 21:31:46 +0000194 Fs_t *fs,
195 char *path)
wdenk591dda52002-11-18 00:14:45 +0000196{
197 int entry;
198 Slot_t SubDir;
199
200 if(path[0] == '\0' || strcmp (path, ".") == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000201 return (0);
wdenk591dda52002-11-18 00:14:45 +0000202 }
wdenk57b2d802003-06-27 21:31:46 +0000203
wdenk591dda52002-11-18 00:14:45 +0000204
205 entry = 0;
206 if (vfat_lookup (parent,
wdenk57b2d802003-06-27 21:31:46 +0000207 fs,
208 &(SubDir.dir),
209 &entry,
210 0,
211 path,
212 ACCEPT_DIR | SINGLE | DO_OPEN,
213 0,
214 &SubDir) == 0) {
215 *parent = SubDir;
216 return (0);
wdenk591dda52002-11-18 00:14:45 +0000217 }
218
219 if (strcmp(path, "..") == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000220 parent -> FileSize = -1;
221 parent -> FirstAbsCluNr = 0;
222 parent -> map = root_map;
223 return (0);
wdenk591dda52002-11-18 00:14:45 +0000224 }
225 return (-1);
226}
227/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000228 * open_file --
wdenk591dda52002-11-18 00:14:45 +0000229 *-----------------------------------------------------------------------------
230 */
231int open_file (Slot_t *file, Directory_t *dir)
232{
233 int first;
234 unsigned long size;
235
236 first = __le16_to_cpu (dir -> start);
237
238 if(first == 0 &&
239 (dir -> attr & ATTR_DIRECTORY) != 0) {
wdenk57b2d802003-06-27 21:31:46 +0000240 file -> FirstAbsCluNr = 0;
241 file -> FileSize = -1;
242 file -> map = root_map;
243 return (0);
wdenk591dda52002-11-18 00:14:45 +0000244 }
wdenk57b2d802003-06-27 21:31:46 +0000245
wdenk591dda52002-11-18 00:14:45 +0000246 if ((dir -> attr & ATTR_DIRECTORY) != 0) {
wdenk57b2d802003-06-27 21:31:46 +0000247 size = (1UL << 31) - 1;
wdenk591dda52002-11-18 00:14:45 +0000248 }
249 else {
wdenk57b2d802003-06-27 21:31:46 +0000250 size = __le32_to_cpu (dir -> size);
wdenk591dda52002-11-18 00:14:45 +0000251 }
252
253 file -> map = normal_map;
254 file -> FirstAbsCluNr = first;
255 file -> PreviousRelCluNr = 0xffff;
256 file -> FileSize = size;
257 return (0);
258}
259/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000260 * read_file --
wdenk591dda52002-11-18 00:14:45 +0000261 *-----------------------------------------------------------------------------
262 */
263int read_file (Fs_t *fs,
wdenk57b2d802003-06-27 21:31:46 +0000264 Slot_t *file,
265 char *buf,
266 int where,
267 int len)
wdenk591dda52002-11-18 00:14:45 +0000268{
269 int pos;
270 int read, nb, sect, offset;
wdenk57b2d802003-06-27 21:31:46 +0000271
wdenk591dda52002-11-18 00:14:45 +0000272 pos = file -> map (fs, file, where, &len);
273 if (pos < 0) {
wdenk57b2d802003-06-27 21:31:46 +0000274 return -1;
wdenk591dda52002-11-18 00:14:45 +0000275 }
276 if (len == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000277 return (0);
wdenk591dda52002-11-18 00:14:45 +0000278 }
279
280 /* Compute sector number */
281 sect = pos / SZ_STD_SECTOR;
282 offset = pos % SZ_STD_SECTOR;
283 read = 0;
wdenk57b2d802003-06-27 21:31:46 +0000284
wdenk591dda52002-11-18 00:14:45 +0000285 if (offset) {
wdenk57b2d802003-06-27 21:31:46 +0000286 /* Read doesn't start at the sector beginning. We need to use our */
287 /* cache */
288 if (sect != cache_sect) {
289 if (dev_read (cache, sect, 1) < 0) {
290 return (-1);
291 }
292 cache_sect = sect;
293 }
294 nb = min (len, SZ_STD_SECTOR - offset);
295
296 memcpy (buf, cache + offset, nb);
297 read += nb;
298 len -= nb;
299 sect += 1;
wdenk591dda52002-11-18 00:14:45 +0000300 }
301
302 if (len > SZ_STD_SECTOR) {
wdenk57b2d802003-06-27 21:31:46 +0000303 nb = (len - 1) / SZ_STD_SECTOR;
304 if (dev_read (buf + read, sect, nb) < 0) {
305 return ((read) ? read : -1);
306 }
307 /* update sector position */
308 sect += nb;
wdenk591dda52002-11-18 00:14:45 +0000309
wdenk57b2d802003-06-27 21:31:46 +0000310 /* Update byte position */
311 nb *= SZ_STD_SECTOR;
312 read += nb;
313 len -= nb;
wdenk591dda52002-11-18 00:14:45 +0000314 }
315
316 if (len) {
wdenk57b2d802003-06-27 21:31:46 +0000317 if (sect != cache_sect) {
318 if (dev_read (cache, sect, 1) < 0) {
319 return ((read) ? read : -1);
320 cache_sect = -1;
321 }
322 cache_sect = sect;
323 }
324
325 memcpy (buf + read, cache, len);
326 read += len;
wdenk591dda52002-11-18 00:14:45 +0000327 }
328 return (read);
329}