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