source: rtems/cpukit/libfs/src/dosfs/fat_file.c @ 89dd2ec4

4.10
Last change on this file since 89dd2ec4 was 89dd2ec4, checked in by Sebastian Huber <sebastian.huber@…>, on 06/03/12 at 11:06:51

dosfs: Fix for no space left on device condition

The file size was wrong in the no space left on device condition. This
resulted in turn in a read of an invalid block which lead to an EIO
error status.

  • Property mode set to 100644
File size: 27.1 KB
Line 
1/*
2 *  fat_file.c
3 *
4 *  General operations on "fat-file"
5 *
6 * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
7 * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
8 *
9 * @(#) $Id$
10 *
11 */
12
13#define MSDOS_TRACE 1
14
15#if HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <fcntl.h>
22#include <unistd.h>
23#include <stdarg.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <assert.h>
27#include <time.h>
28
29#include <rtems/libio_.h>
30
31#include "fat.h"
32#include "fat_fat_operations.h"
33#include "fat_file.h"
34
35static inline void
36_hash_insert(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
37             fat_file_fd_t *el);
38
39static inline void
40_hash_delete(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
41             fat_file_fd_t *el);
42
43static inline int
44_hash_search(
45    rtems_filesystem_mount_table_entry_t  *mt_entry,
46    rtems_chain_control                   *hash,
47    uint32_t                               key1,
48    uint32_t                               key2,
49    fat_file_fd_t                                          **ret
50);
51
52static off_t
53fat_file_lseek(
54    rtems_filesystem_mount_table_entry_t  *mt_entry,
55    fat_file_fd_t                         *fat_fd,
56    uint32_t                               file_cln,
57    uint32_t                              *disk_cln
58);
59
60/* fat_file_open --
61 *     Open fat-file. Two hash tables are accessed by key
62 *     constructed from cluster num and offset of the node (i.e.
63 *     files/directories are distinguished by location on the disk).
64 *     First, hash table("vhash") consists of fat-file descriptors corresponded
65 *     to "valid" files is accessed. Search is made by 2 fields equal to key
66 *     constructed. If descriptor is found in the "vhash" - return it.
67 *     Otherwise search is made in hash table("rhash") consits of fat-file
68 *     descriptors corresponded to "removed-but-still-open" files with the
69 *     same keys.
70 *     If search failed, new fat-file descriptor is added to "vhash"
71 *     with both key fields equal to constructed key. Otherwise new fat-file
72 *     descriptor is added to "vhash" with first key field equal to key
73 *     constructed and the second equal to an unique (unique among all values
74 *     of second key fields) value.
75 *
76 * PARAMETERS:
77 *     mt_entry - mount table entry
78 *     pos      - cluster and offset of the node
79 *     fat_fd   - placeholder for returned fat-file descriptor
80 *
81 * RETURNS:
82 *     RC_OK and pointer to opened descriptor on success, or -1 if error
83 *     occured (errno set appropriately)
84 */
85int
86fat_file_open(
87    rtems_filesystem_mount_table_entry_t  *mt_entry,
88    fat_dir_pos_t                         *dir_pos,
89    fat_file_fd_t                        **fat_fd
90    )
91{
92    int            rc = RC_OK;
93    fat_fs_info_t *fs_info = mt_entry->fs_info;
94    fat_file_fd_t *lfat_fd = NULL;
95    uint32_t       key = 0;
96
97    /* construct key */
98    key = fat_construct_key(mt_entry, &dir_pos->sname);
99
100    /* access "valid" hash table */
101    rc = _hash_search(mt_entry, fs_info->vhash, key, 0, &lfat_fd);
102    if ( rc == RC_OK )
103    {
104        /* return pointer to fat_file_descriptor allocated before */
105        (*fat_fd) = lfat_fd;
106        lfat_fd->links_num++;
107        return rc;
108    }
109
110    /* access "removed-but-still-open" hash table */
111    rc = _hash_search(mt_entry, fs_info->rhash, key, key, &lfat_fd);
112
113    lfat_fd = (*fat_fd) = (fat_file_fd_t*)malloc(sizeof(fat_file_fd_t));
114    if ( lfat_fd == NULL )
115        rtems_set_errno_and_return_minus_one( ENOMEM );
116
117    memset(lfat_fd, 0, sizeof(fat_file_fd_t));
118
119    lfat_fd->links_num = 1;
120    lfat_fd->flags &= ~FAT_FILE_REMOVED;
121    lfat_fd->map.last_cln = FAT_UNDEFINED_VALUE;
122
123    lfat_fd->dir_pos = *dir_pos;
124
125    if ( rc != RC_OK )
126        lfat_fd->ino = key;
127    else
128    {
129        lfat_fd->ino = fat_get_unique_ino(mt_entry);
130
131        if ( lfat_fd->ino == 0 )
132        {
133            free((*fat_fd));
134            /*
135             * XXX: kernel resource is unsufficient, but not the memory,
136             * but there is no suitable errno :(
137             */
138            rtems_set_errno_and_return_minus_one( ENOMEM );
139        }
140    }
141    _hash_insert(fs_info->vhash, key, lfat_fd->ino, lfat_fd);
142
143    /*
144     * other fields of fat-file descriptor will be initialized on upper
145     * level
146     */
147
148    return RC_OK;
149}
150
151
152/* fat_file_reopen --
153 *     Increment by 1 number of links
154 *
155 * PARAMETERS:
156 *     fat_fd - fat-file descriptor
157 *
158 * RETURNS:
159 *     RC_OK
160 */
161int
162fat_file_reopen(fat_file_fd_t *fat_fd)
163{
164    fat_fd->links_num++;
165    return RC_OK;
166}
167
168/* fat_file_close --
169 *     Close fat-file. If count of links to fat-file
170 *     descriptor is greater than 1 (i.e. somebody esle holds pointer
171 *     to this descriptor) just decrement it. Otherwise
172 *     do the following. If this descriptor corresponded to removed fat-file
173 *     then free clusters contained fat-file data, delete descriptor from
174 *     "rhash" table and free memory allocated by descriptor. If descriptor
175 *     correspondes to non-removed fat-file and 'ino' field has value from
176 *     unique inode numbers pool then set count of links to descriptor to zero
177 *     and leave it in hash, otherwise delete descriptor from "vhash" and free
178 *     memory allocated by the descriptor
179 *
180 * PARAMETERS:
181 *     mt_entry - mount table entry
182 *     fat_fd   - fat-file descriptor
183 *
184 * RETURNS:
185 *     RC_OK, or -1 if error occured (errno set appropriately)
186 */
187int
188fat_file_close(
189    rtems_filesystem_mount_table_entry_t *mt_entry,
190    fat_file_fd_t                        *fat_fd
191    )
192{
193    int            rc = RC_OK;
194    fat_fs_info_t *fs_info = mt_entry->fs_info;
195    uint32_t       key = 0;
196
197    /*
198     * if links_num field of fat-file descriptor is greater than 1
199     * decrement the count of links and return
200     */
201    if (fat_fd->links_num > 1)
202    {
203        fat_fd->links_num--;
204        return rc;
205    }
206
207    key = fat_construct_key(mt_entry, &fat_fd->dir_pos.sname);
208
209    if (fat_fd->flags & FAT_FILE_REMOVED)
210    {
211        rc = fat_file_truncate(mt_entry, fat_fd, 0);
212        if ( rc != RC_OK )
213            return rc;
214
215        _hash_delete(fs_info->rhash, key, fat_fd->ino, fat_fd);
216
217        if ( fat_ino_is_unique(mt_entry, fat_fd->ino) )
218            fat_free_unique_ino(mt_entry, fat_fd->ino);
219
220        free(fat_fd);
221    }
222    else
223    {
224        if (fat_ino_is_unique(mt_entry, fat_fd->ino))
225        {
226            fat_fd->links_num = 0;
227        }
228        else
229        {
230            _hash_delete(fs_info->vhash, key, fat_fd->ino, fat_fd);
231            free(fat_fd);
232        }
233    }
234    /*
235     * flush any modified "cached" buffer back to disk
236     */
237    rc = fat_buf_release(fs_info);
238
239    return rc;
240}
241
242/* fat_file_read --
243 *     Read 'count' bytes from 'start' position from fat-file. This
244 *     interface hides the architecture of fat-file, represents it as
245 *     linear file
246 *
247 * PARAMETERS:
248 *     mt_entry - mount table entry
249 *     fat_fd   - fat-file descriptor
250 *     start    - offset in fat-file (in bytes) to read from
251 *     count    - count of bytes to read
252 *     buf      - buffer provided by user
253 *
254 * RETURNS:
255 *     the number of bytes read on success, or -1 if error occured (errno
256 *     set appropriately)
257 */
258ssize_t
259fat_file_read(
260    rtems_filesystem_mount_table_entry_t *mt_entry,
261    fat_file_fd_t                        *fat_fd,
262    uint32_t                              start,
263    uint32_t                              count,
264    uint8_t                              *buf
265)
266{
267    int            rc = RC_OK;
268    ssize_t        ret = 0;
269    fat_fs_info_t *fs_info = mt_entry->fs_info;
270    uint32_t       cmpltd = 0;
271    uint32_t       cur_cln = 0;
272    uint32_t       cl_start = 0;
273    uint32_t       save_cln = 0;
274    uint32_t       ofs = 0;
275    uint32_t       save_ofs;
276    uint32_t       sec = 0;
277    uint32_t       byte = 0;
278    uint32_t       c = 0;
279
280    /* it couldn't be removed - otherwise cache update will be broken */
281    if (count == 0)
282        return cmpltd;
283
284    /*
285     * >= because start is offset and computed from 0 and file_size
286     * computed from 1
287     */
288    if ( start >= fat_fd->fat_file_size )
289        return FAT_EOF;
290
291    if ((count > fat_fd->fat_file_size) ||
292        (start > fat_fd->fat_file_size - count))
293        count = fat_fd->fat_file_size - start;
294
295    if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
296        (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
297    {
298        sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->cln);
299        sec += (start >> fs_info->vol.sec_log2);
300        byte = start & (fs_info->vol.bps - 1);
301
302        ret = _fat_block_read(mt_entry, sec, byte, count, buf);
303        if ( ret < 0 )
304            return -1;
305
306        return ret;
307    }
308
309    cl_start = start >> fs_info->vol.bpc_log2;
310    save_ofs = ofs = start & (fs_info->vol.bpc - 1);
311
312    rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln);
313    if (rc != RC_OK)
314        return rc;
315
316    while (count > 0)
317    {
318        c = MIN(count, (fs_info->vol.bpc - ofs));
319
320        sec = fat_cluster_num_to_sector_num(mt_entry, cur_cln);
321        sec += (ofs >> fs_info->vol.sec_log2);
322        byte = ofs & (fs_info->vol.bps - 1);
323
324        ret = _fat_block_read(mt_entry, sec, byte, c, buf + cmpltd);
325        if ( ret < 0 )
326            return -1;
327
328        count -= c;
329        cmpltd += c;
330        save_cln = cur_cln;
331        rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln);
332        if ( rc != RC_OK )
333            return rc;
334
335        ofs = 0;
336    }
337
338    /* update cache */
339    /* XXX: check this - I'm not sure :( */
340    fat_fd->map.file_cln = cl_start +
341                           ((save_ofs + cmpltd - 1) >> fs_info->vol.bpc_log2);
342    fat_fd->map.disk_cln = save_cln;
343
344    return cmpltd;
345}
346
347/* fat_file_write --
348 *     Write 'count' bytes of data from user supplied buffer to fat-file
349 *     starting at offset 'start'. This interface hides the architecture
350 *     of fat-file, represents it as linear file
351 *
352 * PARAMETERS:
353 *     mt_entry - mount table entry
354 *     fat_fd   - fat-file descriptor
355 *     start    - offset(in bytes) to write from
356 *     count    - count
357 *     buf      - buffer provided by user
358 *
359 * RETURNS:
360 *     number of bytes actually written to the file on success, or -1 if
361 *     error occured (errno set appropriately)
362 */
363ssize_t
364fat_file_write(
365    rtems_filesystem_mount_table_entry_t *mt_entry,
366    fat_file_fd_t                        *fat_fd,
367    uint32_t                              start,
368    uint32_t                              count,
369    const uint8_t                        *buf
370    )
371{
372    int            rc = 0;
373    ssize_t        ret = 0;
374    fat_fs_info_t *fs_info = mt_entry->fs_info;
375    uint32_t       cmpltd = 0;
376    uint32_t       cur_cln = 0;
377    uint32_t       save_cln = 0; /* FIXME: This might be incorrect, cf. below */
378    uint32_t       cl_start = 0;
379    uint32_t       ofs = 0;
380    uint32_t       save_ofs;
381    uint32_t       sec = 0;
382    uint32_t       byte = 0;
383    uint32_t       c = 0;
384
385    if ( count == 0 )
386        return cmpltd;
387
388    if ( start > fat_fd->fat_file_size )
389        rtems_set_errno_and_return_minus_one( EIO );
390
391    if ((count > fat_fd->size_limit) ||
392        (start > fat_fd->size_limit - count))
393        rtems_set_errno_and_return_minus_one( EIO );
394
395    rc = fat_file_extend(mt_entry, fat_fd, start + count, &c);
396    if (rc != RC_OK)
397        return rc;
398
399    /*
400     * check whether there was enough room on device to locate
401     * file of 'start + count' bytes
402     */
403    if (c != (start + count))
404        count = c - start;
405
406    if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
407        (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
408    {
409        sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->cln);
410        sec += (start >> fs_info->vol.sec_log2);
411        byte = start & (fs_info->vol.bps - 1);
412
413        ret = _fat_block_write(mt_entry, sec, byte, count, buf);
414        if ( ret < 0 )
415            return -1;
416
417        return ret;
418    }
419
420    cl_start = start >> fs_info->vol.bpc_log2;
421    save_ofs = ofs = start & (fs_info->vol.bpc - 1);
422
423    rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln);
424    if (rc != RC_OK)
425        return rc;
426
427    while (count > 0)
428    {
429        c = MIN(count, (fs_info->vol.bpc - ofs));
430
431        sec = fat_cluster_num_to_sector_num(mt_entry, cur_cln);
432        sec += (ofs >> fs_info->vol.sec_log2);
433        byte = ofs & (fs_info->vol.bps - 1);
434
435        ret = _fat_block_write(mt_entry, sec, byte, c, buf + cmpltd);
436        if ( ret < 0 )
437            return -1;
438
439        count -= c;
440        cmpltd += c;
441        save_cln = cur_cln;
442        rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln);
443        if ( rc != RC_OK )
444            return rc;
445
446        ofs = 0;
447    }
448
449    /* update cache */
450    /* XXX: check this - I'm not sure :( */
451    fat_fd->map.file_cln = cl_start +
452                           ((save_ofs + cmpltd - 1) >> fs_info->vol.bpc_log2);
453    fat_fd->map.disk_cln = save_cln;
454
455    return cmpltd;
456}
457
458/* fat_file_extend --
459 *     Extend fat-file. If new length less than current fat-file size -
460 *     do nothing. Otherwise calculate necessary count of clusters to add,
461 *     allocate it and add new clusters chain to the end of
462 *     existing clusters chain.
463 *
464 * PARAMETERS:
465 *     mt_entry   - mount table entry
466 *     fat_fd     - fat-file descriptor
467 *     new_length - new length
468 *     a_length   - placeholder for result - actual new length of file
469 *
470 * RETURNS:
471 *     RC_OK and new length of file on success, or -1 if error occured (errno
472 *     set appropriately)
473 */
474int
475fat_file_extend(
476    rtems_filesystem_mount_table_entry_t *mt_entry,
477    fat_file_fd_t                        *fat_fd,
478    uint32_t                              new_length,
479    uint32_t                             *a_length
480    )
481{
482    int            rc = RC_OK;
483    fat_fs_info_t *fs_info = mt_entry->fs_info;
484    uint32_t       chain = 0;
485    uint32_t       bytes2add = 0;
486    uint32_t       cls2add = 0;
487    uint32_t       old_last_cl;
488    uint32_t       last_cl = 0;
489    uint32_t       bytes_remain = 0;
490    uint32_t       cls_added;
491
492    *a_length = new_length;
493
494    if (new_length <= fat_fd->fat_file_size)
495        return RC_OK;
496
497    if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
498        (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
499        rtems_set_errno_and_return_minus_one( ENOSPC );
500
501    bytes_remain = (fs_info->vol.bpc -
502                   (fat_fd->fat_file_size & (fs_info->vol.bpc - 1))) &
503                   (fs_info->vol.bpc - 1);
504
505    bytes2add = new_length - fat_fd->fat_file_size;
506
507    if (bytes2add > bytes_remain)
508        bytes2add -= bytes_remain;
509    else
510        bytes2add = 0;
511
512    /*
513     * if in last cluster allocated for the file there is enough room to
514     * handle extention (hence we don't need to add even one cluster to the
515     * file ) - return
516     */
517    if (bytes2add == 0)
518        return RC_OK;
519
520    cls2add = ((bytes2add - 1) >> fs_info->vol.bpc_log2) + 1;
521
522    rc = fat_scan_fat_for_free_clusters(mt_entry, &chain, cls2add,
523                                        &cls_added, &last_cl);
524
525    /* this means that low level I/O error occured */
526    if (rc != RC_OK)
527        return rc;
528
529    /* this means that no space left on device */
530    if ((cls_added == 0) && (bytes_remain == 0))
531        rtems_set_errno_and_return_minus_one(ENOSPC);
532
533    /*  check wether we satisfied request for 'cls2add' clusters */
534    if (cls2add != cls_added)
535    {
536        new_length -= bytes2add & (fs_info->vol.bpc - 1);
537        new_length -= (cls2add - cls_added) << fs_info->vol.bpc_log2;
538    }
539
540    /* add new chain to the end of existed */
541    if ( fat_fd->fat_file_size == 0 )
542    {
543        fat_fd->map.disk_cln = fat_fd->cln = chain;
544        fat_fd->map.file_cln = 0;
545    }
546    else
547    {
548        if (fat_fd->map.last_cln != FAT_UNDEFINED_VALUE)
549        {
550            old_last_cl = fat_fd->map.last_cln;
551        }
552        else
553        {
554            rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM,
555                                (fat_fd->fat_file_size - 1), &old_last_cl);
556            if ( rc != RC_OK )
557            {
558                fat_free_fat_clusters_chain(mt_entry, chain);
559                return rc;
560            }
561        }
562
563        rc = fat_set_fat_cluster(mt_entry, old_last_cl, chain);
564        if ( rc != RC_OK )
565        {
566            fat_free_fat_clusters_chain(mt_entry, chain);
567            return rc;
568        }
569        fat_buf_release(fs_info);
570    }
571
572    /* update number of the last cluster of the file if it changed */
573    if (cls_added != 0)
574    {
575        fat_fd->map.last_cln = last_cl;
576        if (fat_fd->fat_file_type == FAT_DIRECTORY)
577        {
578            rc = fat_init_clusters_chain(mt_entry, chain);
579            if ( rc != RC_OK )
580            {
581                fat_free_fat_clusters_chain(mt_entry, chain);
582                return rc;
583            }
584        }
585    }
586
587    *a_length = new_length;
588    fat_fd->fat_file_size = new_length;
589
590    return RC_OK;
591}
592
593/* fat_file_truncate --
594 *     Truncate fat-file. If new length greater than current fat-file size -
595 *     do nothing. Otherwise find first cluster to free and free all clusters
596 *     in the chain starting from this cluster.
597 *
598 * PARAMETERS:
599 *     mt_entry   - mount table entry
600 *     fat_fd     - fat-file descriptor
601 *     new_length - new length
602 *
603 * RETURNS:
604 *     RC_OK on success, or -1 if error occured (errno set appropriately)
605 */
606int
607fat_file_truncate(
608    rtems_filesystem_mount_table_entry_t *mt_entry,
609    fat_file_fd_t                        *fat_fd,
610    uint32_t                              new_length
611    )
612{
613    int            rc = RC_OK;
614    fat_fs_info_t *fs_info = mt_entry->fs_info;
615    uint32_t       cur_cln = 0;
616    uint32_t       cl_start = 0;
617    uint32_t       new_last_cln = FAT_UNDEFINED_VALUE;
618
619
620    if ( new_length >= fat_fd->fat_file_size )
621        return rc;
622
623    assert(fat_fd->fat_file_size);
624
625    cl_start = (new_length + fs_info->vol.bpc - 1) >> fs_info->vol.bpc_log2;
626
627    if ((cl_start << fs_info->vol.bpc_log2) >= fat_fd->fat_file_size)
628        return RC_OK;
629
630    if (cl_start != 0)
631    {
632        rc = fat_file_lseek(mt_entry, fat_fd, cl_start - 1, &new_last_cln);
633        if (rc != RC_OK)
634            return rc;
635
636    }
637
638    rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln);
639    if (rc != RC_OK)
640        return rc;
641
642    rc = fat_free_fat_clusters_chain(mt_entry, cur_cln);
643    if (rc != RC_OK)
644        return rc;
645
646    if (cl_start != 0)
647    {
648        rc = fat_set_fat_cluster(mt_entry, new_last_cln, FAT_GENFAT_EOC);
649        if ( rc != RC_OK )
650            return rc;
651        fat_fd->map.file_cln = cl_start - 1;
652        fat_fd->map.disk_cln = new_last_cln;
653        fat_fd->map.last_cln = new_last_cln;
654    }
655    return RC_OK;
656}
657
658/* fat_file_ioctl --
659 *     F_CLU_NUM:
660 *         make mapping between serial number of the cluster in fat-file and
661 *         its real number on the volume
662 *
663 * PARAMETERS:
664 *     fat_fd     - fat-file descriptor
665 *     mt_entry   - mount table entry
666 *     cmd        - command
667 *     ...
668 *
669 * RETURNS:
670 *     RC_OK on success, or -1 if error occured and errno set appropriately
671 */
672int
673fat_file_ioctl(
674    rtems_filesystem_mount_table_entry_t *mt_entry,
675    fat_file_fd_t                        *fat_fd,
676    int                                   cmd,
677    ...)
678{
679    int            rc = RC_OK;
680    fat_fs_info_t *fs_info = mt_entry->fs_info;
681    uint32_t       cur_cln = 0;
682    uint32_t       cl_start = 0;
683    uint32_t       pos = 0;
684    uint32_t      *ret;
685    va_list        ap;
686
687    va_start(ap, cmd);
688
689    switch (cmd)
690    {
691        case F_CLU_NUM:
692            pos = va_arg(ap, uint32_t  );
693            ret = va_arg(ap, uint32_t   *);
694
695            /* sanity check */
696            if ( pos >= fat_fd->fat_file_size )
697                rtems_set_errno_and_return_minus_one( EIO );
698
699            if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
700                (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
701            {
702                /* cluster 0 (zero) reserved for root dir */
703                *ret  = 0;
704                return RC_OK;
705            }
706
707            cl_start = pos >> fs_info->vol.bpc_log2;
708
709            rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln);
710            if ( rc != RC_OK )
711                return rc;
712
713            *ret = cur_cln;
714            break;
715
716        default:
717            errno = EINVAL;
718            rc = -1;
719            break;
720    }
721    return rc;
722}
723
724/* fat_file_mark_removed --
725 *     Remove the fat-file descriptor from "valid" hash table, insert it
726 *     into "removed-but-still-open" hash table and set up "removed" bit.
727 *
728 * PARAMETERS:
729 *     fat_fd     - fat-file descriptor
730 *     mt_entry   - mount table entry
731 *
732 * RETURNS:
733 *     None
734 */
735void
736fat_file_mark_removed(
737    rtems_filesystem_mount_table_entry_t *mt_entry,
738    fat_file_fd_t                        *fat_fd
739    )
740{
741    fat_fs_info_t *fs_info = mt_entry->fs_info;
742    uint32_t       key = 0;
743
744    key = fat_construct_key(mt_entry, &fat_fd->dir_pos.sname);
745
746    _hash_delete(fs_info->vhash, key, fat_fd->ino, fat_fd);
747
748    _hash_insert(fs_info->rhash, key, fat_fd->ino, fat_fd);
749
750    fat_fd->flags |= FAT_FILE_REMOVED;
751}
752
753/* fat_file_datasync --
754 *     Synchronize fat-file -  flush all buffered data to the media.
755 *
756 * PARAMETERS:
757 *     mt_entry - mount table entry
758 *     fat_fd   - fat-file descriptor
759 *
760 * RETURNS:
761 *     RC_OK on success, or -1 if error occured and errno set appropriately
762 */
763int
764fat_file_datasync(
765    rtems_filesystem_mount_table_entry_t *mt_entry,
766    fat_file_fd_t                        *fat_fd
767    )
768{
769    int                 rc = RC_OK;
770    rtems_status_code   sc = RTEMS_SUCCESSFUL;
771    fat_fs_info_t      *fs_info = mt_entry->fs_info;
772    uint32_t            cur_cln = fat_fd->cln;
773    rtems_bdbuf_buffer *block = NULL;
774    uint32_t            sec = 0;
775    uint32_t            i = 0;
776
777    if (fat_fd->fat_file_size == 0)
778        return RC_OK;
779
780    /*
781     * we can use only one bdbuf :( and we also know that cache is useless
782     * for sync operation, so don't use it
783     */
784    rc = fat_buf_release(fs_info);
785    if (rc != RC_OK)
786        return rc;
787
788    /* for each cluster of the file ... */
789    while ((cur_cln & fs_info->vol.mask) < fs_info->vol.eoc_val)
790    {
791        sec = fat_cluster_num_to_sector_num(mt_entry, cur_cln);
792        /* for each sector in cluster ... */
793        for ( i = 0; i < fs_info->vol.spc; i++ )
794        {
795            /* ... sync it */
796            sc = rtems_bdbuf_read(fs_info->vol.dev, (sec + i), &block);
797            if (sc != RTEMS_SUCCESSFUL)
798                rtems_set_errno_and_return_minus_one( EIO );
799
800            sc = rtems_bdbuf_sync(block);
801            if ( sc != RTEMS_SUCCESSFUL )
802                rtems_set_errno_and_return_minus_one( EIO );
803        }
804
805        rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln);
806        if ( rc != RC_OK )
807            return rc;
808    }
809    return rc;
810}
811
812/* fat_file_size --
813 *     Calculate fat-file size - fat-file is nothing that clusters chain, so
814 *     go through all clusters in the chain and count it. Only
815 *     special case is root directory for FAT12/16 volumes.
816 *     This function is used only for directories which are fat-files with
817 *     non-zero length, hence 'fat_fd->cln' always contains valid data.
818 *     Calculated size is stored in 'fat_file_size' field of fat-file
819 *     descriptor.
820 *
821 * PARAMETERS:
822 *     mt_entry - mount table entry
823 *     fat_fd   - fat-file descriptor
824 *
825 * RETURNS:
826 *     RC_OK on success, or -1 if error occured (errno set appropriately)
827 */
828int
829fat_file_size(
830    rtems_filesystem_mount_table_entry_t *mt_entry,
831    fat_file_fd_t                        *fat_fd
832    )
833{
834    int            rc = RC_OK;
835    fat_fs_info_t *fs_info = mt_entry->fs_info;
836    uint32_t       cur_cln = fat_fd->cln;
837    uint32_t       save_cln = 0;
838
839    /* Have we requested root dir size for FAT12/16? */
840    if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
841        (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
842    {
843        fat_fd->fat_file_size = fs_info->vol.rdir_size;
844        return rc;
845    }
846
847    fat_fd->fat_file_size = 0;
848
849    while ((cur_cln & fs_info->vol.mask) < fs_info->vol.eoc_val)
850    {
851        save_cln = cur_cln;
852        rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln);
853        if ( rc != RC_OK )
854            return rc;
855
856        fat_fd->fat_file_size += fs_info->vol.bpc;
857    }
858    fat_fd->map.last_cln = save_cln;
859    return rc;
860}
861
862/* hash support routines */
863
864/* _hash_insert --
865 *     Insert elemnt into hash based on key 'key1'
866 *
867 * PARAMETERS:
868 *     hash - hash element will be inserted into
869 *     key1 - key on which insertion is based on
870 *     key2 - not used during insertion
871 *     el   - element to insert
872 *
873 * RETURNS:
874 *     None
875 */
876static inline void
877_hash_insert(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
878             fat_file_fd_t *el)
879{
880    rtems_chain_append((hash) + ((key1) % FAT_HASH_MODULE), &(el)->link);
881}
882
883
884/* _hash_delete --
885 *     Remove element from hash
886 *
887 * PARAMETERS:
888 *     hash - hash element will be removed from
889 *     key1 - not used
890 *     key2 - not used
891 *     el   - element to delete
892 *
893 * RETURNS:
894 *     None
895 */
896static inline void
897_hash_delete(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
898             fat_file_fd_t *el)
899{
900    rtems_chain_extract(&(el)->link);
901}
902
903/* _hash_search --
904 *     Search element in hash. If both keys match pointer to found element
905 *     is returned
906 *
907 * PARAMETERS:
908 *     mt_entry - mount table entry
909 *     hash     - hash element will be removed from
910 *     key1     - search key
911 *     key2     - search key
912 *     ret      - placeholder for result
913 *
914 * RETURNS:
915 *     0 and pointer to found element on success, -1 otherwise
916 */
917static inline int
918_hash_search(
919    rtems_filesystem_mount_table_entry_t  *mt_entry,
920    rtems_chain_control                   *hash,
921    uint32_t                               key1,
922    uint32_t                               key2,
923    fat_file_fd_t                          **ret
924    )
925{
926    uint32_t          mod = (key1) % FAT_HASH_MODULE;
927    rtems_chain_node *the_node = ((rtems_chain_control *)((hash) + mod))->first;
928
929    for ( ; !rtems_chain_is_tail((hash) + mod, the_node) ; )
930    {
931        fat_file_fd_t *ffd = (fat_file_fd_t *)the_node;
932        uint32_t       ck =  fat_construct_key(mt_entry, &ffd->dir_pos.sname);
933
934        if ( (key1) == ck)
935        {
936            if ( ((key2) == 0) || ((key2) == ffd->ino) )
937            {
938                *ret = (void *)the_node;
939                return 0;
940            }
941        }
942        the_node = the_node->next;
943    }
944    return -1;
945}
946
947static off_t
948fat_file_lseek(
949    rtems_filesystem_mount_table_entry_t  *mt_entry,
950    fat_file_fd_t                         *fat_fd,
951    uint32_t                               file_cln,
952    uint32_t                              *disk_cln
953    )
954{
955    int rc = RC_OK;
956
957    if (file_cln == fat_fd->map.file_cln)
958        *disk_cln = fat_fd->map.disk_cln;
959    else
960    {
961        uint32_t   cur_cln;
962        uint32_t   count;
963        uint32_t   i;
964
965        if (file_cln > fat_fd->map.file_cln)
966        {
967            cur_cln = fat_fd->map.disk_cln;
968            count = file_cln - fat_fd->map.file_cln;
969        }
970        else
971        {
972            cur_cln = fat_fd->cln;
973            count = file_cln;
974        }
975
976        /* skip over the clusters */
977        for (i = 0; i < count; i++)
978        {
979            rc = fat_get_fat_cluster(mt_entry, cur_cln, &cur_cln);
980            if ( rc != RC_OK )
981                return rc;
982        }
983
984        /* update cache */
985        fat_fd->map.file_cln = file_cln;
986        fat_fd->map.disk_cln = cur_cln;
987
988        *disk_cln = cur_cln;
989    }
990    return RC_OK;
991}
Note: See TracBrowser for help on using the repository browser.