Rev 3 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | pj | 1 | /* |
2 | * |
||
3 | * |
||
4 | * |
||
5 | * |
||
6 | */ |
||
7 | |||
8 | #include <fs/maccess.h> |
||
9 | #include <fs/util.h> |
||
10 | #include <fs/errno.h> |
||
11 | #include <fs/stat.h> |
||
12 | #include <fs/dirent.h> |
||
13 | #include <fs/fcntl.h> |
||
14 | |||
15 | #include "dcache.h" |
||
16 | #include "fs.h" |
||
17 | #include "msdos.h" |
||
18 | #include "fsconst.h" |
||
19 | |||
20 | #include "file.h" |
||
21 | #include "fileop.h" |
||
22 | #include "msdos/msdos_f.h" |
||
23 | |||
24 | #include "debug.h" |
||
25 | |||
26 | /* debug __position() */ |
||
27 | #define DEBUG_POSITION KERN_DEBUG |
||
28 | #undef DEBUG_POSITION |
||
29 | |||
30 | /* debug __increase() */ |
||
31 | #define DEBUG_INCREASE KERN_DEBUG |
||
32 | #undef DEBUG_INCREASE |
||
33 | |||
34 | /* |
||
35 | * |
||
36 | */ |
||
37 | |||
38 | #ifdef DEBUG_POSITION |
||
39 | #define printk3a(fmt,args...) \ |
||
40 | if (debug_info) printk(DEBUG_POSITION fmt,##args) |
||
41 | #else |
||
42 | #define printk3a(fmt,args...) |
||
43 | #endif |
||
44 | |||
45 | #ifdef DEBUG_INCREASE |
||
46 | #define _printk1(fmt,args...) \ |
||
47 | if (debug_info) printk(DEBUG_INCREASE fmt,##args) |
||
48 | #else |
||
49 | #define _printk1(fmt,args...) |
||
50 | #endif |
||
51 | |||
52 | static inline void startoffile(struct inode *in, struct file *f) |
||
53 | { |
||
54 | MSDOS_F(f).cluster=MSDOS_I(in).scluster; |
||
55 | MSDOS_F(f).bakcluster=NO_CLUSTER; |
||
56 | MSDOS_F(f).lsector=msdos_cluster2sector(in,MSDOS_F(f).cluster); |
||
57 | if (MSDOS_F(f).cluster!=FREE_CLUSTER) |
||
58 | _assert(MSDOS_F(f).lsector!=(__uint32_t)-1); |
||
59 | MSDOS_F(f).sectnum=0; |
||
60 | MSDOS_F(f).bpos=0; |
||
61 | } |
||
62 | |||
63 | /* |
||
64 | * |
||
65 | * |
||
66 | */ |
||
67 | |||
68 | static int msdos_open(struct inode *in, struct file *f) |
||
69 | { |
||
70 | startoffile(in,f); |
||
71 | f->f_pos=0; |
||
72 | printk0("msdos_open: starting (cluster 0x%lx) (lsect %li)", |
||
73 | (long)MSDOS_I(in).scluster, |
||
74 | (long)MSDOS_F(f).lsector); |
||
75 | return EOK; |
||
76 | } |
||
77 | |||
78 | static void __position(struct file *f,__off_t pos) |
||
79 | { |
||
80 | struct inode *in=f->f_dentry->d_inode; |
||
81 | struct super_block *sb=f->f_dentry->d_sb; |
||
82 | __uint32_t nc; |
||
83 | |||
84 | /* it can be optimized: |
||
85 | * if the new position is after the actual position we |
||
86 | * can not go thought all the file list chain |
||
87 | */ |
||
88 | |||
89 | /* from 0 (start-of-file) to in->i_st.st_size (end-of-file) */ |
||
90 | _assert(pos>=0&&pos<=in->i_st.st_size); |
||
91 | |||
92 | /* remeber that f->f_pos is the position (in bytes) of the next |
||
93 | * bytes to read/write |
||
94 | * so |
||
95 | * if we are at end-of-file the bytes is not into the file |
||
96 | */ |
||
97 | |||
98 | printk3a("__position: START"); |
||
99 | |||
100 | f->f_pos=pos; |
||
101 | startoffile(in,f); |
||
102 | |||
103 | nc=pos/(MSDOS_SB(sb).spc*SECTORSIZE); /* number of cluster to skip */ |
||
104 | pos-=nc*(MSDOS_SB(sb).spc*SECTORSIZE); |
||
105 | printk3a("__position: cluster to skip %li",(long)nc); |
||
106 | printk3a("__position: position into cluster %li",(long)pos); |
||
107 | |||
108 | /* very good :-( I must go thought the list chain! */ |
||
109 | while (nc>0) { |
||
110 | MSDOS_F(f).bakcluster=MSDOS_F(f).cluster; |
||
111 | MSDOS_F(f).cluster=msdos_nextcluster(sb,MSDOS_F(f).cluster); |
||
112 | nc--; |
||
113 | } |
||
114 | |||
115 | MSDOS_F(f).sectnum=pos/SECTORSIZE; |
||
116 | pos-=MSDOS_F(f).sectnum*SECTORSIZE; |
||
117 | printk3a("__position: sector number %i",MSDOS_F(f).sectnum); |
||
118 | |||
119 | MSDOS_F(f).lsector=msdos_cluster2sector(in,MSDOS_F(f).cluster); |
||
120 | _assert(MSDOS_F(f).lsector!=(__uint32_t)-1); |
||
121 | MSDOS_F(f).lsector+=MSDOS_F(f).sectnum; |
||
122 | |||
123 | MSDOS_F(f).bpos=pos; |
||
124 | printk3a("__position: position into sector %li",(long)pos); |
||
125 | printk3a("__position: logical sector %li",(long)MSDOS_F(f).lsector); |
||
126 | |||
127 | printk3a("__position: END"); |
||
128 | } |
||
129 | |||
130 | static __off_t msdos_lseek(struct file *f, __off_t off, int whence) |
||
131 | { |
||
132 | struct inode *in=f->f_dentry->d_inode; |
||
133 | __off_t pos; |
||
134 | |||
135 | /* da fare il test sulla fine cluster!!!! */ |
||
136 | |||
137 | printk3("msdos_lseek: START"); |
||
138 | |||
139 | printk3("msdos_lseek: current position %li",(long)f->f_pos); |
||
140 | switch (whence) { |
||
141 | case SEEK_SET: pos=off; break; |
||
142 | case SEEK_CUR: pos=f->f_pos+off; break; |
||
143 | case SEEK_END: pos=in->i_st.st_size+off; break; |
||
144 | default: |
||
145 | printk3("msdos_lseek: END"); |
||
146 | return -EINVAL; |
||
147 | } |
||
148 | printk3("msdos_lseek: next position %li",(long)pos); |
||
149 | |||
150 | if (pos<0) { |
||
151 | printk3("msdos_lseek: out of position range"); |
||
152 | printk3("msdos_lseek: END"); |
||
153 | return -ESPIPE; /* it is "illegal seek" */ |
||
154 | } |
||
155 | |||
156 | f->f_pos=pos; |
||
157 | |||
158 | if (f->f_pos>in->i_st.st_size) { |
||
159 | printk3("msdos_lseek: after end-of-file"); |
||
160 | printk3("msdos_lseek: END"); |
||
161 | return f->f_pos; |
||
162 | } |
||
163 | |||
164 | __position(f,pos); |
||
165 | |||
166 | printk3("msdos_lseek: END"); |
||
167 | return f->f_pos; |
||
168 | } |
||
169 | |||
170 | static int msdos_close(struct inode *in, struct file *f) |
||
171 | { |
||
172 | return EOK; |
||
173 | } |
||
174 | |||
175 | /* --- */ |
||
176 | |||
177 | /* advance the file pointer by sz bytes */ |
||
178 | /* PS: sz must NOT cross sectors (see msdos_read())*/ |
||
179 | static void __inline__ __advance(struct file *f, |
||
180 | struct super_block *sb, |
||
181 | struct inode *in, |
||
182 | int sz) |
||
183 | { |
||
184 | f->f_pos+=sz; |
||
185 | if (sz==SECTORSIZE-MSDOS_F(f).bpos) { |
||
186 | MSDOS_F(f).bpos=0; |
||
187 | MSDOS_F(f).lsector++; |
||
188 | MSDOS_F(f).sectnum++; |
||
189 | |||
190 | if (MSDOS_I(in).scluster==ROOT_CLUSTER) return; |
||
191 | |||
192 | if (MSDOS_F(f).sectnum==MSDOS_SB(sb).spc) { |
||
193 | MSDOS_F(f).sectnum=0; |
||
194 | |||
195 | //printk(KERN_DEBUG "act cluster 0x%04li",(long)MSDOS_F(f).cluster); |
||
196 | |||
197 | MSDOS_F(f).bakcluster=MSDOS_F(f).cluster; |
||
198 | MSDOS_F(f).cluster=msdos_nextcluster(sb,MSDOS_F(f).cluster); |
||
199 | if (MSDOS_F(f).cluster!=NO_CLUSTER) { |
||
200 | MSDOS_F(f).lsector=msdos_cluster2sector(in,MSDOS_F(f).cluster); |
||
201 | _assert(MSDOS_F(f).lsector!=(__uint32_t)-1); |
||
202 | } |
||
203 | |||
204 | //printk(KERN_DEBUG "new cluster 0x%04li",(long)MSDOS_F(f).cluster); |
||
205 | |||
206 | } |
||
207 | } else { |
||
208 | MSDOS_F(f).bpos+=sz; |
||
209 | } |
||
210 | } |
||
211 | |||
212 | /* increase the file size by delta (we must be on end-of-file) */ |
||
213 | static __ssize_t __inline__ __increase(struct file *f, |
||
214 | struct super_block *sb, |
||
215 | struct inode *in, |
||
216 | __ssize_t delta) |
||
217 | { |
||
218 | __uint16_t scluster,cluster; |
||
219 | __ssize_t l; |
||
220 | long i,n; |
||
221 | |||
222 | _printk1("__increase: START"); |
||
223 | _printk1("__increase: clusters act:0x%04x bak:0x%04x", |
||
224 | MSDOS_F(f).cluster,MSDOS_F(f).bakcluster); |
||
225 | |||
226 | /* |
||
227 | * MSDOS_F(f).bakcluster contains the previous cluster of the actual |
||
228 | * file position; |
||
229 | * it is used only into when MSDOS_F(f).cluster==NO_CLUSTER |
||
230 | * (we are at end-of-file of a file that is multiple of a cluster) |
||
231 | */ |
||
232 | |||
233 | _assert(delta!=0); |
||
234 | if (MSDOS_I(in).scluster==ROOT_CLUSTER) return 0; |
||
235 | |||
236 | if (MSDOS_F(f).cluster==FREE_CLUSTER) { |
||
237 | /* we must allocate a cluster */ |
||
238 | /* we do not have any cluster in this chain! */ |
||
239 | _printk1("__increase: free cluster"); |
||
240 | MSDOS_F(f).cluster=msdos_addcluster(sb,NO_CLUSTER); |
||
241 | if (MSDOS_F(f).cluster==NO_CLUSTER) return 0; |
||
242 | MSDOS_F(f).lsector=msdos_cluster2sector(in,MSDOS_F(f).cluster); |
||
243 | _assert(MSDOS_F(f).lsector!=(__uint32_t)-1); |
||
244 | MSDOS_I(in).scluster=MSDOS_F(f).cluster; |
||
245 | in->i_dirty=1; |
||
246 | } |
||
247 | |||
248 | if (MSDOS_F(f).cluster==NO_CLUSTER) { |
||
249 | /* we must allocate a cluster */ |
||
250 | /* we are at end-of-file on a cluster boundary! */ |
||
251 | _printk1("__increase: no cluster"); |
||
252 | _assert(MSDOS_F(f).bakcluster!=NO_CLUSTER); |
||
253 | MSDOS_F(f).cluster=msdos_addcluster(sb,MSDOS_F(f).bakcluster); |
||
254 | if (MSDOS_F(f).cluster==NO_CLUSTER) return 0; |
||
255 | MSDOS_F(f).lsector=msdos_cluster2sector(in,MSDOS_F(f).cluster); |
||
256 | _assert(MSDOS_F(f).lsector!=(__uint32_t)-1); |
||
257 | } |
||
258 | |||
259 | _printk1("__increase: compute space available"); |
||
260 | |||
261 | /* space available (to the end of cluster) */ |
||
262 | l=((__ssize_t)MSDOS_SB(sb).spc-(__ssize_t)MSDOS_F(f).sectnum-1)*SECTORSIZE; |
||
263 | l+=(SECTORSIZE-(__ssize_t)MSDOS_F(f).bpos); |
||
264 | |||
265 | if ((long)delta-(long)l>0) { |
||
266 | /* some other clusters needed */ |
||
267 | n=(delta-l)/(SECTORSIZE*MSDOS_SB(sb).spc)+1; |
||
268 | scluster=cluster=MSDOS_F(f).cluster; |
||
269 | |||
270 | _printk1("__increase: other clusters needed"); |
||
271 | |||
272 | for (i=0;i<n;i++) { |
||
273 | _printk1("__increase: added cluster from 0x%04x",cluster); |
||
274 | cluster=msdos_addcluster(sb,cluster); |
||
275 | if (cluster==NO_CLUSTER) { |
||
276 | /* we shall allow an allocation lesser than requested */ |
||
277 | delta=l+i*SECTORSIZE*MSDOS_SB(sb).spc; |
||
278 | break; |
||
279 | } |
||
280 | } |
||
281 | |||
282 | } |
||
283 | |||
284 | if (delta>0) { |
||
285 | _printk1("__increase: delta>0 => dirty inode"); |
||
286 | in->i_st.st_size+=delta; |
||
287 | in->i_dirty=1; |
||
288 | } |
||
289 | _printk1("__increase: END"); |
||
290 | return delta; |
||
291 | } |
||
292 | |||
293 | /* |
||
294 | * |
||
295 | */ |
||
296 | |||
297 | static __ssize_t msdos_read(struct file *f, char *p, __ssize_t d) |
||
298 | { |
||
299 | struct inode *in=f->f_dentry->d_inode; |
||
300 | struct super_block *sb=f->f_dentry->d_sb; |
||
301 | dcache_t *buf; |
||
302 | int sz; |
||
303 | __ssize_t bc; |
||
304 | |||
305 | //printk(KERN_DEBUG "reading %i bytes from position %li",d,(long)f->f_pos); |
||
306 | |||
307 | if (d==0) return 0; |
||
308 | |||
309 | /* if we are at or after the end-of-file */ |
||
310 | if (f->f_pos>=in->i_st.st_size) return 0; |
||
311 | |||
312 | bc=0; |
||
313 | while (bc<d&&in->i_st.st_size>f->f_pos) { |
||
314 | buf=dcache_lock(sb->sb_dev,MSDOS_F(f).lsector); |
||
315 | if (buf==NULL) return -EIO; |
||
316 | sz=min(d-bc,SECTORSIZE-MSDOS_F(f).bpos); |
||
317 | sz=min(sz,in->i_st.st_size-f->f_pos); |
||
318 | memcpytouser(p,buf->buffer+MSDOS_F(f).bpos,sz); |
||
319 | dcache_unlock(buf); |
||
320 | p+=sz; |
||
321 | bc+=sz; |
||
322 | __advance(f,sb,in,sz); |
||
323 | } |
||
324 | |||
325 | return bc; |
||
326 | } |
||
327 | |||
328 | static __ssize_t msdos_write(struct file *f, char *p, __ssize_t d) |
||
329 | { |
||
330 | struct inode *in=f->f_dentry->d_inode; |
||
331 | struct super_block *sb=f->f_dentry->d_sb; |
||
332 | dcache_t *buf; |
||
333 | int sz; |
||
334 | __ssize_t bc; |
||
335 | |||
336 | //printk(KERN_DEBUG "writing %i bytes from position %li",d,(long)f->f_pos); |
||
337 | |||
338 | if (d==0) return 0; |
||
339 | |||
340 | /* O_APPEND flag - test for safety */ |
||
341 | /* |
||
342 | if (f->f_flags&O_APPEND) |
||
343 | if (f->f_pos!=in->i_st.st_size) { |
||
344 | __position(f,in->i_st.st_size); |
||
345 | } |
||
346 | */ |
||
347 | |||
348 | if (f->f_pos>in->i_st.st_size) { |
||
349 | /* lseek() has moved the file position AFTER the end of file !!*/ |
||
350 | |||
351 | __ssize_t inc,delta; |
||
352 | |||
353 | //printk(KERN_DEBUG "lseek() after end of file!"); |
||
354 | |||
355 | delta=f->f_pos-in->i_st.st_size; |
||
356 | __position(f,in->i_st.st_size); |
||
357 | inc=__increase(f,sb,in,delta); |
||
358 | if (inc!=delta) return -EIO; |
||
359 | |||
360 | bc=0; |
||
361 | while (bc<delta) { |
||
362 | buf=dcache_acquire(sb->sb_dev,MSDOS_F(f).lsector); |
||
363 | if (buf==NULL) return -EIO; |
||
364 | dcache_dirty(buf); |
||
365 | sz=min(delta-bc,SECTORSIZE-MSDOS_F(f).bpos); |
||
366 | sz=min(sz,in->i_st.st_size-f->f_pos); |
||
367 | memset(buf->buffer+MSDOS_F(f).bpos,0,sz); |
||
368 | dcache_release(buf); |
||
369 | bc+=sz; |
||
370 | __advance(f,sb,in,sz); |
||
371 | } |
||
372 | |||
373 | } |
||
374 | |||
375 | /* |
||
376 | printk(KERN_DEBUG "pos: %li d: %li act size: %li", |
||
377 | (long)f->f_pos,(long)d,(long)in->i_st.st_size); |
||
378 | */ |
||
379 | |||
380 | if (f->f_pos+d>in->i_st.st_size) { |
||
381 | /* no room for the data that must be written */ |
||
382 | __ssize_t inc; |
||
383 | |||
384 | //printk(KERN_DEBUG "increase request" ); |
||
385 | |||
386 | inc=__increase(f,sb,in,f->f_pos+d-in->i_st.st_size); |
||
387 | if (inc==0) return -EIO; |
||
388 | d=inc+in->i_st.st_size-f->f_pos; |
||
389 | |||
390 | //printk(KERN_DEBUG "new size: %li",(long)in->i_st.st_size); |
||
391 | } |
||
392 | |||
393 | //printk(KERN_DEBUG "init writing"); |
||
394 | |||
395 | bc=0; |
||
396 | while (bc<d&&in->i_st.st_size>f->f_pos) { |
||
397 | buf=dcache_acquire(sb->sb_dev,MSDOS_F(f).lsector); |
||
398 | if (buf==NULL) { |
||
399 | //printk(KERN_DEBUG "can't aquire!"); |
||
400 | return -EIO; |
||
401 | } |
||
402 | //printk(KERN_DEBUG "write on %li",MSDOS_F(f).lsector); |
||
403 | dcache_dirty(buf); |
||
404 | sz=min(d-bc,SECTORSIZE-MSDOS_F(f).bpos); |
||
405 | sz=min(sz,in->i_st.st_size-f->f_pos); |
||
406 | memcpyfromuser(buf->buffer+MSDOS_F(f).bpos,p,sz); |
||
407 | dcache_release(buf); |
||
408 | p+=sz; |
||
409 | bc+=sz; |
||
410 | __advance(f,sb,in,sz); |
||
411 | } |
||
412 | |||
413 | return bc; |
||
414 | } |
||
415 | |||
416 | #ifdef DEBUG_READDIR_DUMP |
||
417 | #define dump_directory_entry(ptr) msdos_dump_direntry(ptr) |
||
418 | #else |
||
419 | #define dump_directory_entry(ptr) |
||
420 | #endif |
||
421 | |||
422 | //int startcounter=0; |
||
423 | //int counter; |
||
424 | |||
425 | static int msdos_readdir(struct file *f, void *den) |
||
426 | { |
||
427 | struct super_block *sb=f->f_dentry->d_sb; |
||
428 | struct inode *in=f->f_dentry->d_inode; |
||
429 | struct directoryentry *de; |
||
430 | struct dirent ude; |
||
431 | dcache_t *buf=NULL; |
||
432 | struct qstr name; |
||
433 | __uint32_t lsect; |
||
434 | int res; |
||
435 | |||
436 | //debug_check_mutex(__FILE__,__LINE__,501); |
||
437 | |||
438 | printk5("msdos_readdir: START"); |
||
439 | |||
440 | if (MSDOS_F(f).cluster==NO_CLUSTER) { |
||
441 | printk5("msdos_readdir: end of cluster (no more entries)"); |
||
442 | printk5("msdos_readdir: END"); |
||
443 | return 1; |
||
444 | } |
||
445 | |||
446 | lsect=-1; |
||
447 | for (;;) { |
||
448 | |||
449 | if (MSDOS_F(f).lsector!=lsect) { |
||
450 | if (lsect!=-1) dcache_unlock(buf); |
||
451 | buf=dcache_lock(sb->sb_dev,MSDOS_F(f).lsector); |
||
452 | if (buf==NULL) { |
||
453 | //printk(KERN_ERR "msdos_readdir: can't lock sector %li", |
||
454 | // (long)MSDOS_F(f).lsector); |
||
455 | printk5("msdos_readdir: END"); |
||
456 | return -EIO; |
||
457 | } |
||
458 | lsect=MSDOS_F(f).lsector; |
||
459 | printk5("msdos_readdir: read %li logical sector",(long int)lsect); |
||
460 | } |
||
461 | |||
462 | de=(struct directoryentry *)(buf->buffer+MSDOS_F(f).bpos); |
||
463 | dump_directory_entry(de); |
||
464 | |||
465 | /* |
||
466 | for debug purpose |
||
467 | if (startcounter) { |
||
468 | if (--counter==0) return 1; |
||
469 | } else |
||
470 | |||
471 | if (msdos_islastentry(de)) { |
||
472 | startcounter=1; |
||
473 | counter=1; |
||
474 | } |
||
475 | */ |
||
476 | |||
477 | if (msdos_islastentry(de)) { |
||
478 | printk5("msdos_readdir: last entry found"); |
||
479 | printk5("msdos_readdir: END"); |
||
480 | // *(int*)&ude=0; |
||
481 | dcache_unlock(buf); |
||
482 | return 1; |
||
483 | } |
||
484 | |||
485 | __advance(f,sb,in,sizeof(struct directoryentry)); |
||
486 | |||
487 | if (!msdos_isunvalidentry(de)) { |
||
488 | res=msdos_formatname(de,&name); |
||
489 | |||
490 | /* there's no need to check for result, but in future... */ |
||
491 | if (res!=0) { |
||
492 | printk5("msdos_readdir: entry found BUT can't format the name"); |
||
493 | printk5("msdos_readdir: END"); |
||
494 | dcache_unlock(buf); |
||
495 | return -EIO; |
||
496 | } |
||
497 | |||
498 | strcpy(ude.d_name,QSTRNAME(&name)); |
||
499 | printk5("msdos_readdir: entry found '%s'",ude.d_name); |
||
500 | break; |
||
501 | } |
||
502 | printk5("msdos_readdir: invalid entry found"); |
||
503 | } |
||
504 | dcache_unlock(buf); |
||
505 | |||
506 | __copy_to_user(den,&ude,sizeof(struct dirent)); |
||
507 | printk5("msdos_readdir: END"); |
||
508 | return EOK; |
||
509 | } |
||
510 | |||
511 | |||
512 | /*--------*/ |
||
513 | |||
514 | struct file_operations msdos_file_ro_operations={ |
||
515 | msdos_lseek, |
||
516 | msdos_read, |
||
517 | dummy_write, |
||
518 | msdos_readdir, |
||
519 | msdos_open, |
||
520 | msdos_close |
||
521 | }; |
||
522 | |||
523 | struct file_operations msdos_file_rw_operations={ |
||
524 | msdos_lseek, |
||
525 | msdos_read, |
||
526 | msdos_write, |
||
527 | msdos_readdir, |
||
528 | msdos_open, |
||
529 | msdos_close |
||
530 | }; |