blob: 497f554f915c3ef572b16f2507ea0bced54ba360 [file] [log] [blame]
wdenk591dda52002-11-18 00:14:45 +00001/*
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 <malloc.h>
28
Jon Loeliger80eb47c2007-07-09 17:56:50 -050029#if defined(CONFIG_CMD_FDOS)
wdenk591dda52002-11-18 00:14:45 +000030
31#include "dos.h"
32#include "fdos.h"
33
34static int cache_sect;
35static unsigned char cache [SZ_STD_SECTOR];
36
37
38#define min(x,y) ((x)<(y)?(x):(y))
39
40static int descend (Slot_t *parent,
wdenk57b2d802003-06-27 21:31:46 +000041 Fs_t *fs,
42 char *path);
wdenk591dda52002-11-18 00:14:45 +000043
44/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000045 * init_subdir --
wdenk591dda52002-11-18 00:14:45 +000046 *-----------------------------------------------------------------------------
47 */
48void init_subdir (void)
49{
50 cache_sect = -1;
51}
52/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000053 * basename --
wdenk591dda52002-11-18 00:14:45 +000054 *-----------------------------------------------------------------------------
55 */
56char *basename (char *name)
57{
58 register char *cptr;
59
60 if (!name || !*name) {
wdenk57b2d802003-06-27 21:31:46 +000061 return ("");
wdenk591dda52002-11-18 00:14:45 +000062 }
wdenk57b2d802003-06-27 21:31:46 +000063
wdenk591dda52002-11-18 00:14:45 +000064 for (cptr= name; *cptr++; );
65 while (--cptr >= name) {
66 if (*cptr == '/') {
wdenk57b2d802003-06-27 21:31:46 +000067 return (cptr + 1);
wdenk591dda52002-11-18 00:14:45 +000068 }
69 }
70 return(name);
71}
72/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000073 * root_map --
wdenk591dda52002-11-18 00:14:45 +000074 *-----------------------------------------------------------------------------
75 */
76static int root_map (Fs_t *fs, Slot_t *file, int where, int *len)
77{
78 *len = min (*len, fs -> dir_len * SZ_STD_SECTOR - where);
79 if (*len < 0 ) {
wdenk57b2d802003-06-27 21:31:46 +000080 *len = 0;
81 return (-1);
wdenk591dda52002-11-18 00:14:45 +000082 }
83 return fs -> dir_start * SZ_STD_SECTOR + where;
84}
85/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000086 * normal_map --
wdenk591dda52002-11-18 00:14:45 +000087 *-----------------------------------------------------------------------------
88 */
89static int normal_map (Fs_t *fs, Slot_t *file, int where, int *len)
90{
91 int offset;
92 int NrClu;
93 unsigned short RelCluNr;
94 unsigned short CurCluNr;
95 unsigned short NewCluNr;
96 unsigned short AbsCluNr;
97 int clus_size;
98
99 clus_size = fs -> cluster_size * SZ_STD_SECTOR;
100 offset = where % clus_size;
101
102 *len = min (*len, file -> FileSize - where);
103
104 if (*len < 0 ) {
wdenk57b2d802003-06-27 21:31:46 +0000105 *len = 0;
106 return (0);
wdenk591dda52002-11-18 00:14:45 +0000107 }
108
109 if (file -> FirstAbsCluNr < 2){
wdenk57b2d802003-06-27 21:31:46 +0000110 *len = 0;
111 return (0);
wdenk591dda52002-11-18 00:14:45 +0000112 }
113
114 RelCluNr = where / clus_size;
wdenk57b2d802003-06-27 21:31:46 +0000115
wdenk591dda52002-11-18 00:14:45 +0000116 if (RelCluNr >= file -> PreviousRelCluNr){
wdenk57b2d802003-06-27 21:31:46 +0000117 CurCluNr = file -> PreviousRelCluNr;
118 AbsCluNr = file -> PreviousAbsCluNr;
wdenk591dda52002-11-18 00:14:45 +0000119 } else {
wdenk57b2d802003-06-27 21:31:46 +0000120 CurCluNr = 0;
121 AbsCluNr = file -> FirstAbsCluNr;
wdenk591dda52002-11-18 00:14:45 +0000122 }
123
124
125 NrClu = (offset + *len - 1) / clus_size;
126 while (CurCluNr <= RelCluNr + NrClu) {
wdenk57b2d802003-06-27 21:31:46 +0000127 if (CurCluNr == RelCluNr){
128 /* we have reached the beginning of our zone. Save
129 * coordinates */
130 file -> PreviousRelCluNr = RelCluNr;
131 file -> PreviousAbsCluNr = AbsCluNr;
132 }
133 NewCluNr = fat_decode (fs, AbsCluNr);
134 if (NewCluNr == 1 || NewCluNr == 0) {
135 PRINTF("Fat problem while decoding %d %x\n",
136 AbsCluNr, NewCluNr);
137 return (-1);
138 }
139 if (CurCluNr == RelCluNr + NrClu) {
140 break;
141 }
wdenk591dda52002-11-18 00:14:45 +0000142
wdenk57b2d802003-06-27 21:31:46 +0000143 if (CurCluNr < RelCluNr && NewCluNr == FAT12_END) {
144 *len = 0;
145 return 0;
146 }
wdenk591dda52002-11-18 00:14:45 +0000147
wdenk57b2d802003-06-27 21:31:46 +0000148 if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1)
149 break;
150 CurCluNr++;
151 AbsCluNr = NewCluNr;
wdenk591dda52002-11-18 00:14:45 +0000152 }
153
154 *len = min (*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
155
156 return (((file -> PreviousAbsCluNr - 2) * fs -> cluster_size +
wdenk57b2d802003-06-27 21:31:46 +0000157 fs -> dir_start + fs -> dir_len) *
158 SZ_STD_SECTOR + offset);
wdenk591dda52002-11-18 00:14:45 +0000159}
160/*-----------------------------------------------------------------------------
161 * open_subdir -- open the subdir containing the file
162 *-----------------------------------------------------------------------------
163 */
164int open_subdir (File_t *desc)
165{
166 char *pathname;
167 char *tmp, *s, *path;
168 char terminator;
wdenk57b2d802003-06-27 21:31:46 +0000169
wdenk591dda52002-11-18 00:14:45 +0000170 if ((pathname = (char *)malloc (MAX_PATH)) == NULL) {
wdenk57b2d802003-06-27 21:31:46 +0000171 return (-1);
wdenk591dda52002-11-18 00:14:45 +0000172 }
wdenk57b2d802003-06-27 21:31:46 +0000173
wdenk591dda52002-11-18 00:14:45 +0000174 strcpy (pathname, desc -> name);
wdenk57b2d802003-06-27 21:31:46 +0000175
wdenk591dda52002-11-18 00:14:45 +0000176 /* Suppress file name */
177 tmp = basename (pathname);
178 *tmp = '\0';
179
180 /* root directory init */
181 desc -> subdir.FirstAbsCluNr = 0;
182 desc -> subdir.FileSize = -1;
183 desc -> subdir.map = root_map;
184 desc -> subdir.dir.attr = ATTR_DIRECTORY;
wdenk57b2d802003-06-27 21:31:46 +0000185
wdenk591dda52002-11-18 00:14:45 +0000186 tmp = pathname;
187 for (s = tmp; ; ++s) {
wdenk57b2d802003-06-27 21:31:46 +0000188 if (*s == '/' || *s == '\0') {
189 path = tmp;
190 terminator = *s;
191 *s = '\0';
192 if (s != tmp && strcmp (path,".")) {
193 if (descend (&desc -> subdir, desc -> fs, path) < 0) {
194 free (pathname);
195 return (-1);
196 }
197 }
198 if (terminator == 0) {
199 break;
200 }
201 tmp = s + 1;
202 }
wdenk591dda52002-11-18 00:14:45 +0000203 }
204 free (pathname);
205 return (0);
206}
207/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000208 * descend --
wdenk591dda52002-11-18 00:14:45 +0000209 *-----------------------------------------------------------------------------
210 */
211static int descend (Slot_t *parent,
wdenk57b2d802003-06-27 21:31:46 +0000212 Fs_t *fs,
213 char *path)
wdenk591dda52002-11-18 00:14:45 +0000214{
215 int entry;
216 Slot_t SubDir;
217
218 if(path[0] == '\0' || strcmp (path, ".") == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000219 return (0);
wdenk591dda52002-11-18 00:14:45 +0000220 }
wdenk57b2d802003-06-27 21:31:46 +0000221
wdenk591dda52002-11-18 00:14:45 +0000222
223 entry = 0;
224 if (vfat_lookup (parent,
wdenk57b2d802003-06-27 21:31:46 +0000225 fs,
226 &(SubDir.dir),
227 &entry,
228 0,
229 path,
230 ACCEPT_DIR | SINGLE | DO_OPEN,
231 0,
232 &SubDir) == 0) {
233 *parent = SubDir;
234 return (0);
wdenk591dda52002-11-18 00:14:45 +0000235 }
236
237 if (strcmp(path, "..") == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000238 parent -> FileSize = -1;
239 parent -> FirstAbsCluNr = 0;
240 parent -> map = root_map;
241 return (0);
wdenk591dda52002-11-18 00:14:45 +0000242 }
243 return (-1);
244}
245/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000246 * open_file --
wdenk591dda52002-11-18 00:14:45 +0000247 *-----------------------------------------------------------------------------
248 */
249int open_file (Slot_t *file, Directory_t *dir)
250{
251 int first;
252 unsigned long size;
253
254 first = __le16_to_cpu (dir -> start);
255
256 if(first == 0 &&
257 (dir -> attr & ATTR_DIRECTORY) != 0) {
wdenk57b2d802003-06-27 21:31:46 +0000258 file -> FirstAbsCluNr = 0;
259 file -> FileSize = -1;
260 file -> map = root_map;
261 return (0);
wdenk591dda52002-11-18 00:14:45 +0000262 }
wdenk57b2d802003-06-27 21:31:46 +0000263
wdenk591dda52002-11-18 00:14:45 +0000264 if ((dir -> attr & ATTR_DIRECTORY) != 0) {
wdenk57b2d802003-06-27 21:31:46 +0000265 size = (1UL << 31) - 1;
wdenk591dda52002-11-18 00:14:45 +0000266 }
267 else {
wdenk57b2d802003-06-27 21:31:46 +0000268 size = __le32_to_cpu (dir -> size);
wdenk591dda52002-11-18 00:14:45 +0000269 }
270
271 file -> map = normal_map;
272 file -> FirstAbsCluNr = first;
273 file -> PreviousRelCluNr = 0xffff;
274 file -> FileSize = size;
275 return (0);
276}
277/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000278 * read_file --
wdenk591dda52002-11-18 00:14:45 +0000279 *-----------------------------------------------------------------------------
280 */
281int read_file (Fs_t *fs,
wdenk57b2d802003-06-27 21:31:46 +0000282 Slot_t *file,
283 char *buf,
284 int where,
285 int len)
wdenk591dda52002-11-18 00:14:45 +0000286{
287 int pos;
288 int read, nb, sect, offset;
wdenk57b2d802003-06-27 21:31:46 +0000289
wdenk591dda52002-11-18 00:14:45 +0000290 pos = file -> map (fs, file, where, &len);
291 if (pos < 0) {
wdenk57b2d802003-06-27 21:31:46 +0000292 return -1;
wdenk591dda52002-11-18 00:14:45 +0000293 }
294 if (len == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000295 return (0);
wdenk591dda52002-11-18 00:14:45 +0000296 }
297
298 /* Compute sector number */
299 sect = pos / SZ_STD_SECTOR;
300 offset = pos % SZ_STD_SECTOR;
301 read = 0;
wdenk57b2d802003-06-27 21:31:46 +0000302
wdenk591dda52002-11-18 00:14:45 +0000303 if (offset) {
wdenk57b2d802003-06-27 21:31:46 +0000304 /* Read doesn't start at the sector beginning. We need to use our */
305 /* cache */
306 if (sect != cache_sect) {
307 if (dev_read (cache, sect, 1) < 0) {
308 return (-1);
309 }
310 cache_sect = sect;
311 }
312 nb = min (len, SZ_STD_SECTOR - offset);
313
314 memcpy (buf, cache + offset, nb);
315 read += nb;
316 len -= nb;
317 sect += 1;
wdenk591dda52002-11-18 00:14:45 +0000318 }
319
320 if (len > SZ_STD_SECTOR) {
wdenk57b2d802003-06-27 21:31:46 +0000321 nb = (len - 1) / SZ_STD_SECTOR;
322 if (dev_read (buf + read, sect, nb) < 0) {
323 return ((read) ? read : -1);
324 }
325 /* update sector position */
326 sect += nb;
wdenk591dda52002-11-18 00:14:45 +0000327
wdenk57b2d802003-06-27 21:31:46 +0000328 /* Update byte position */
329 nb *= SZ_STD_SECTOR;
330 read += nb;
331 len -= nb;
wdenk591dda52002-11-18 00:14:45 +0000332 }
333
334 if (len) {
wdenk57b2d802003-06-27 21:31:46 +0000335 if (sect != cache_sect) {
336 if (dev_read (cache, sect, 1) < 0) {
337 return ((read) ? read : -1);
338 cache_sect = -1;
339 }
340 cache_sect = sect;
341 }
342
343 memcpy (buf + read, cache, len);
344 read += len;
wdenk591dda52002-11-18 00:14:45 +0000345 }
346 return (read);
347}
348#endif