blob: 5e96b0a65dd71ddfe76bcb221b182986902b0cdf [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
wdenk591dda52002-11-18 00:14:45 +000029#include "dos.h"
30#include "fdos.h"
31
32static int cache_sect;
33static unsigned char cache [SZ_STD_SECTOR];
34
35
36#define min(x,y) ((x)<(y)?(x):(y))
37
38static int descend (Slot_t *parent,
wdenk57b2d802003-06-27 21:31:46 +000039 Fs_t *fs,
40 char *path);
wdenk591dda52002-11-18 00:14:45 +000041
42/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000043 * init_subdir --
wdenk591dda52002-11-18 00:14:45 +000044 *-----------------------------------------------------------------------------
45 */
46void init_subdir (void)
47{
48 cache_sect = -1;
49}
50/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000051 * basename --
wdenk591dda52002-11-18 00:14:45 +000052 *-----------------------------------------------------------------------------
53 */
54char *basename (char *name)
55{
56 register char *cptr;
57
58 if (!name || !*name) {
wdenk57b2d802003-06-27 21:31:46 +000059 return ("");
wdenk591dda52002-11-18 00:14:45 +000060 }
wdenk57b2d802003-06-27 21:31:46 +000061
wdenk591dda52002-11-18 00:14:45 +000062 for (cptr= name; *cptr++; );
63 while (--cptr >= name) {
64 if (*cptr == '/') {
wdenk57b2d802003-06-27 21:31:46 +000065 return (cptr + 1);
wdenk591dda52002-11-18 00:14:45 +000066 }
67 }
68 return(name);
69}
70/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000071 * root_map --
wdenk591dda52002-11-18 00:14:45 +000072 *-----------------------------------------------------------------------------
73 */
74static int root_map (Fs_t *fs, Slot_t *file, int where, int *len)
75{
76 *len = min (*len, fs -> dir_len * SZ_STD_SECTOR - where);
77 if (*len < 0 ) {
wdenk57b2d802003-06-27 21:31:46 +000078 *len = 0;
79 return (-1);
wdenk591dda52002-11-18 00:14:45 +000080 }
81 return fs -> dir_start * SZ_STD_SECTOR + where;
82}
83/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +000084 * normal_map --
wdenk591dda52002-11-18 00:14:45 +000085 *-----------------------------------------------------------------------------
86 */
87static int normal_map (Fs_t *fs, Slot_t *file, int where, int *len)
88{
89 int offset;
90 int NrClu;
91 unsigned short RelCluNr;
92 unsigned short CurCluNr;
93 unsigned short NewCluNr;
94 unsigned short AbsCluNr;
95 int clus_size;
96
97 clus_size = fs -> cluster_size * SZ_STD_SECTOR;
98 offset = where % clus_size;
99
100 *len = min (*len, file -> FileSize - where);
101
102 if (*len < 0 ) {
wdenk57b2d802003-06-27 21:31:46 +0000103 *len = 0;
104 return (0);
wdenk591dda52002-11-18 00:14:45 +0000105 }
106
107 if (file -> FirstAbsCluNr < 2){
wdenk57b2d802003-06-27 21:31:46 +0000108 *len = 0;
109 return (0);
wdenk591dda52002-11-18 00:14:45 +0000110 }
111
112 RelCluNr = where / clus_size;
wdenk57b2d802003-06-27 21:31:46 +0000113
wdenk591dda52002-11-18 00:14:45 +0000114 if (RelCluNr >= file -> PreviousRelCluNr){
wdenk57b2d802003-06-27 21:31:46 +0000115 CurCluNr = file -> PreviousRelCluNr;
116 AbsCluNr = file -> PreviousAbsCluNr;
wdenk591dda52002-11-18 00:14:45 +0000117 } else {
wdenk57b2d802003-06-27 21:31:46 +0000118 CurCluNr = 0;
119 AbsCluNr = file -> FirstAbsCluNr;
wdenk591dda52002-11-18 00:14:45 +0000120 }
121
122
123 NrClu = (offset + *len - 1) / clus_size;
124 while (CurCluNr <= RelCluNr + NrClu) {
wdenk57b2d802003-06-27 21:31:46 +0000125 if (CurCluNr == RelCluNr){
126 /* we have reached the beginning of our zone. Save
127 * coordinates */
128 file -> PreviousRelCluNr = RelCluNr;
129 file -> PreviousAbsCluNr = AbsCluNr;
130 }
131 NewCluNr = fat_decode (fs, AbsCluNr);
132 if (NewCluNr == 1 || NewCluNr == 0) {
133 PRINTF("Fat problem while decoding %d %x\n",
134 AbsCluNr, NewCluNr);
135 return (-1);
136 }
137 if (CurCluNr == RelCluNr + NrClu) {
138 break;
139 }
wdenk591dda52002-11-18 00:14:45 +0000140
wdenk57b2d802003-06-27 21:31:46 +0000141 if (CurCluNr < RelCluNr && NewCluNr == FAT12_END) {
142 *len = 0;
143 return 0;
144 }
wdenk591dda52002-11-18 00:14:45 +0000145
wdenk57b2d802003-06-27 21:31:46 +0000146 if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1)
147 break;
148 CurCluNr++;
149 AbsCluNr = NewCluNr;
wdenk591dda52002-11-18 00:14:45 +0000150 }
151
152 *len = min (*len, (1 + CurCluNr - RelCluNr) * clus_size - offset);
153
154 return (((file -> PreviousAbsCluNr - 2) * fs -> cluster_size +
wdenk57b2d802003-06-27 21:31:46 +0000155 fs -> dir_start + fs -> dir_len) *
156 SZ_STD_SECTOR + offset);
wdenk591dda52002-11-18 00:14:45 +0000157}
158/*-----------------------------------------------------------------------------
159 * open_subdir -- open the subdir containing the file
160 *-----------------------------------------------------------------------------
161 */
162int open_subdir (File_t *desc)
163{
164 char *pathname;
165 char *tmp, *s, *path;
166 char terminator;
wdenk57b2d802003-06-27 21:31:46 +0000167
wdenk591dda52002-11-18 00:14:45 +0000168 if ((pathname = (char *)malloc (MAX_PATH)) == NULL) {
wdenk57b2d802003-06-27 21:31:46 +0000169 return (-1);
wdenk591dda52002-11-18 00:14:45 +0000170 }
wdenk57b2d802003-06-27 21:31:46 +0000171
wdenk591dda52002-11-18 00:14:45 +0000172 strcpy (pathname, desc -> name);
wdenk57b2d802003-06-27 21:31:46 +0000173
wdenk591dda52002-11-18 00:14:45 +0000174 /* Suppress file name */
175 tmp = basename (pathname);
176 *tmp = '\0';
177
178 /* root directory init */
179 desc -> subdir.FirstAbsCluNr = 0;
180 desc -> subdir.FileSize = -1;
181 desc -> subdir.map = root_map;
182 desc -> subdir.dir.attr = ATTR_DIRECTORY;
wdenk57b2d802003-06-27 21:31:46 +0000183
wdenk591dda52002-11-18 00:14:45 +0000184 tmp = pathname;
185 for (s = tmp; ; ++s) {
wdenk57b2d802003-06-27 21:31:46 +0000186 if (*s == '/' || *s == '\0') {
187 path = tmp;
188 terminator = *s;
189 *s = '\0';
190 if (s != tmp && strcmp (path,".")) {
191 if (descend (&desc -> subdir, desc -> fs, path) < 0) {
192 free (pathname);
193 return (-1);
194 }
195 }
196 if (terminator == 0) {
197 break;
198 }
199 tmp = s + 1;
200 }
wdenk591dda52002-11-18 00:14:45 +0000201 }
202 free (pathname);
203 return (0);
204}
205/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000206 * descend --
wdenk591dda52002-11-18 00:14:45 +0000207 *-----------------------------------------------------------------------------
208 */
209static int descend (Slot_t *parent,
wdenk57b2d802003-06-27 21:31:46 +0000210 Fs_t *fs,
211 char *path)
wdenk591dda52002-11-18 00:14:45 +0000212{
213 int entry;
214 Slot_t SubDir;
215
216 if(path[0] == '\0' || strcmp (path, ".") == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000217 return (0);
wdenk591dda52002-11-18 00:14:45 +0000218 }
wdenk57b2d802003-06-27 21:31:46 +0000219
wdenk591dda52002-11-18 00:14:45 +0000220
221 entry = 0;
222 if (vfat_lookup (parent,
wdenk57b2d802003-06-27 21:31:46 +0000223 fs,
224 &(SubDir.dir),
225 &entry,
226 0,
227 path,
228 ACCEPT_DIR | SINGLE | DO_OPEN,
229 0,
230 &SubDir) == 0) {
231 *parent = SubDir;
232 return (0);
wdenk591dda52002-11-18 00:14:45 +0000233 }
234
235 if (strcmp(path, "..") == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000236 parent -> FileSize = -1;
237 parent -> FirstAbsCluNr = 0;
238 parent -> map = root_map;
239 return (0);
wdenk591dda52002-11-18 00:14:45 +0000240 }
241 return (-1);
242}
243/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000244 * open_file --
wdenk591dda52002-11-18 00:14:45 +0000245 *-----------------------------------------------------------------------------
246 */
247int open_file (Slot_t *file, Directory_t *dir)
248{
249 int first;
250 unsigned long size;
251
252 first = __le16_to_cpu (dir -> start);
253
254 if(first == 0 &&
255 (dir -> attr & ATTR_DIRECTORY) != 0) {
wdenk57b2d802003-06-27 21:31:46 +0000256 file -> FirstAbsCluNr = 0;
257 file -> FileSize = -1;
258 file -> map = root_map;
259 return (0);
wdenk591dda52002-11-18 00:14:45 +0000260 }
wdenk57b2d802003-06-27 21:31:46 +0000261
wdenk591dda52002-11-18 00:14:45 +0000262 if ((dir -> attr & ATTR_DIRECTORY) != 0) {
wdenk57b2d802003-06-27 21:31:46 +0000263 size = (1UL << 31) - 1;
wdenk591dda52002-11-18 00:14:45 +0000264 }
265 else {
wdenk57b2d802003-06-27 21:31:46 +0000266 size = __le32_to_cpu (dir -> size);
wdenk591dda52002-11-18 00:14:45 +0000267 }
268
269 file -> map = normal_map;
270 file -> FirstAbsCluNr = first;
271 file -> PreviousRelCluNr = 0xffff;
272 file -> FileSize = size;
273 return (0);
274}
275/*-----------------------------------------------------------------------------
wdenk57b2d802003-06-27 21:31:46 +0000276 * read_file --
wdenk591dda52002-11-18 00:14:45 +0000277 *-----------------------------------------------------------------------------
278 */
279int read_file (Fs_t *fs,
wdenk57b2d802003-06-27 21:31:46 +0000280 Slot_t *file,
281 char *buf,
282 int where,
283 int len)
wdenk591dda52002-11-18 00:14:45 +0000284{
285 int pos;
286 int read, nb, sect, offset;
wdenk57b2d802003-06-27 21:31:46 +0000287
wdenk591dda52002-11-18 00:14:45 +0000288 pos = file -> map (fs, file, where, &len);
289 if (pos < 0) {
wdenk57b2d802003-06-27 21:31:46 +0000290 return -1;
wdenk591dda52002-11-18 00:14:45 +0000291 }
292 if (len == 0) {
wdenk57b2d802003-06-27 21:31:46 +0000293 return (0);
wdenk591dda52002-11-18 00:14:45 +0000294 }
295
296 /* Compute sector number */
297 sect = pos / SZ_STD_SECTOR;
298 offset = pos % SZ_STD_SECTOR;
299 read = 0;
wdenk57b2d802003-06-27 21:31:46 +0000300
wdenk591dda52002-11-18 00:14:45 +0000301 if (offset) {
wdenk57b2d802003-06-27 21:31:46 +0000302 /* Read doesn't start at the sector beginning. We need to use our */
303 /* cache */
304 if (sect != cache_sect) {
305 if (dev_read (cache, sect, 1) < 0) {
306 return (-1);
307 }
308 cache_sect = sect;
309 }
310 nb = min (len, SZ_STD_SECTOR - offset);
311
312 memcpy (buf, cache + offset, nb);
313 read += nb;
314 len -= nb;
315 sect += 1;
wdenk591dda52002-11-18 00:14:45 +0000316 }
317
318 if (len > SZ_STD_SECTOR) {
wdenk57b2d802003-06-27 21:31:46 +0000319 nb = (len - 1) / SZ_STD_SECTOR;
320 if (dev_read (buf + read, sect, nb) < 0) {
321 return ((read) ? read : -1);
322 }
323 /* update sector position */
324 sect += nb;
wdenk591dda52002-11-18 00:14:45 +0000325
wdenk57b2d802003-06-27 21:31:46 +0000326 /* Update byte position */
327 nb *= SZ_STD_SECTOR;
328 read += nb;
329 len -= nb;
wdenk591dda52002-11-18 00:14:45 +0000330 }
331
332 if (len) {
wdenk57b2d802003-06-27 21:31:46 +0000333 if (sect != cache_sect) {
334 if (dev_read (cache, sect, 1) < 0) {
335 return ((read) ? read : -1);
336 cache_sect = -1;
337 }
338 cache_sect = sect;
339 }
340
341 memcpy (buf + read, cache, len);
342 read += len;
wdenk591dda52002-11-18 00:14:45 +0000343 }
344 return (read);
345}