source: rtems/cpukit/libfs/src/dosfs/fat_file.c @ 063eb574

5
Last change on this file since 063eb574 was 063eb574, checked in by Sebastian Huber <sebastian.huber@…>, on 03/14/17 at 07:26:40

dosfs: Simplify fat_file_open()

Update #2929.

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