source: rtems/cpukit/libfs/src/dosfs/fat_file.c @ 389ffb28

4.115
Last change on this file since 389ffb28 was 389ffb28, checked in by Sebastian Huber <sebastian.huber@…>, on 08/21/13 at 12:58:26

dosfs: Use unprotected chain operations

This area is protected by the FAT file system instance lock.

  • Property mode set to 100644
File size: 28.9 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*)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(fs_info);
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 *     fs_info  - FS info
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    fat_fs_info_t                        *fs_info,
190    fat_file_fd_t                        *fat_fd
191    )
192{
193    int            rc = RC_OK;
194
195    /*
196     * if links_num field of fat-file descriptor is greater than 1
197     * decrement only the count of links
198     */
199    if (fat_fd->links_num > 1)
200    {
201        fat_fd->links_num--;
202    }
203    else
204    {
205        uint32_t key = fat_construct_key(fs_info, &fat_fd->dir_pos.sname);
206
207        if (fat_fd->flags & FAT_FILE_REMOVED)
208        {
209            rc = fat_file_truncate(fs_info, fat_fd, 0);
210            if (rc == RC_OK)
211            {
212                _hash_delete(fs_info->rhash, key, fat_fd->ino, fat_fd);
213
214                if (fat_ino_is_unique(fs_info, fat_fd->ino))
215                    fat_free_unique_ino(fs_info, fat_fd->ino);
216
217                free(fat_fd);
218            }
219        }
220        else
221        {
222            if (fat_ino_is_unique(fs_info, fat_fd->ino))
223            {
224                fat_fd->links_num = 0;
225            }
226            else
227            {
228                _hash_delete(fs_info->vhash, key, fat_fd->ino, fat_fd);
229                free(fat_fd);
230            }
231        }
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 *     fs_info  - FS info
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    fat_fs_info_t                        *fs_info,
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    uint32_t       cmpltd = 0;
270    uint32_t       cur_cln = 0;
271    uint32_t       cl_start = 0;
272    uint32_t       save_cln = 0;
273    uint32_t       ofs = 0;
274    uint32_t       save_ofs;
275    uint32_t       sec = 0;
276    uint32_t       byte = 0;
277    uint32_t       c = 0;
278
279    /* it couldn't be removed - otherwise cache update will be broken */
280    if (count == 0)
281        return cmpltd;
282
283    /*
284     * >= because start is offset and computed from 0 and file_size
285     * computed from 1
286     */
287    if ( start >= fat_fd->fat_file_size )
288        return FAT_EOF;
289
290    if ((count > fat_fd->fat_file_size) ||
291        (start > fat_fd->fat_file_size - count))
292        count = fat_fd->fat_file_size - start;
293
294    if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
295        (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
296    {
297        sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->cln);
298        sec += (start >> fs_info->vol.sec_log2);
299        byte = start & (fs_info->vol.bps - 1);
300
301        ret = _fat_block_read(fs_info, sec, byte, count, buf);
302        if ( ret < 0 )
303            return -1;
304
305        return ret;
306    }
307
308    cl_start = start >> fs_info->vol.bpc_log2;
309    save_ofs = ofs = start & (fs_info->vol.bpc - 1);
310
311    rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
312    if (rc != RC_OK)
313        return rc;
314
315    while (count > 0)
316    {
317        c = MIN(count, (fs_info->vol.bpc - ofs));
318
319        sec = fat_cluster_num_to_sector_num(fs_info, cur_cln);
320        sec += (ofs >> fs_info->vol.sec_log2);
321        byte = ofs & (fs_info->vol.bps - 1);
322
323        ret = _fat_block_read(fs_info, sec, byte, c, buf + cmpltd);
324        if ( ret < 0 )
325            return -1;
326
327        count -= c;
328        cmpltd += c;
329        save_cln = cur_cln;
330        rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
331        if ( rc != RC_OK )
332            return rc;
333
334        ofs = 0;
335    }
336
337    /* update cache */
338    /* XXX: check this - I'm not sure :( */
339    fat_fd->map.file_cln = cl_start +
340                           ((save_ofs + cmpltd - 1) >> fs_info->vol.bpc_log2);
341    fat_fd->map.disk_cln = save_cln;
342
343    return cmpltd;
344}
345
346/* fat_is_fat12_or_fat16_root_dir --
347 *     Returns true for FAT12 root directories respectively FAT16
348 *     root directories. Returns false for everything else.
349 *
350 *  PARAMETERS:
351 *      fat_fd        - fat-file descriptor
352 *      volume_type   - type of fat volume: FAT_FAT12 or FAT_FAT16 or FAT_FAT32
353 *
354 *  RETURNS:
355 *      true if conditions for FAT12 root directory or FAT16 root directory
356 *      match, false if not
357 */
358static bool
359 fat_is_fat12_or_fat16_root_dir (const fat_file_fd_t *fat_fd,
360                                 const uint8_t volume_type)
361{
362    return (FAT_FD_OF_ROOT_DIR(fat_fd)) && (volume_type & (FAT_FAT12 | FAT_FAT16));
363}
364
365/* fat_file_write_fat32_or_non_root_dir --
366 *     Execute fat file write for FAT32 respectively for non-root
367 *     directories of FAT12 or FAT16
368 *
369 * PARAMETERS:
370 *     fs_info          - FS info
371 *     fat_fd           - fat-file descriptor
372 *     start            - offset(in bytes) to write from
373 *     count            - count
374 *     buf              - buffer provided by user
375 *     file_cln_initial - initial current cluster number of the file
376 *
377 * RETURNS:
378 *     number of bytes actually written to the file on success, or -1 if
379 *     error occured (errno set appropriately)
380 */
381static ssize_t
382 fat_file_write_fat32_or_non_root_dir(
383     fat_fs_info_t                        *fs_info,
384     fat_file_fd_t                        *fat_fd,
385     const uint32_t                        start,
386     const uint32_t                        count,
387     const uint8_t                        *buf,
388     const uint32_t                        file_cln_initial)
389{
390    int            rc = RC_OK;
391    uint32_t       cmpltd = 0;
392    uint32_t       cur_cln = 0;
393    uint32_t       save_cln = 0; /* FIXME: This might be incorrect, cf. below */
394    uint32_t       start_cln = start >> fs_info->vol.bpc_log2;
395    uint32_t       ofs_cln = start - (start_cln << fs_info->vol.bpc_log2);
396    uint32_t       ofs_cln_save = ofs_cln;
397    uint32_t       bytes_to_write = count;
398    uint32_t       file_cln_cnt;
399    ssize_t        ret;
400    uint32_t       c;
401    bool           overwrite_cluster = false;
402
403    rc = fat_file_lseek(fs_info, fat_fd, start_cln, &cur_cln);
404    if (RC_OK == rc)
405    {
406        file_cln_cnt = cur_cln - fat_fd->cln;
407        while (   (RC_OK == rc)
408               && (bytes_to_write > 0))
409        {
410            c = MIN(bytes_to_write, (fs_info->vol.bpc - ofs_cln));
411
412            if (file_cln_initial < file_cln_cnt)
413                overwrite_cluster = true;
414
415            ret = fat_cluster_write(fs_info,
416                                      cur_cln,
417                                      ofs_cln,
418                                      c,
419                                      &buf[cmpltd],
420                                      overwrite_cluster);
421            if (0 > ret)
422              rc = -1;
423
424            if (RC_OK == rc)
425            {
426                ++file_cln_cnt;
427                bytes_to_write -= ret;
428                cmpltd += ret;
429                save_cln = cur_cln;
430                if (0 < bytes_to_write)
431                  rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
432
433                ofs_cln = 0;
434            }
435        }
436
437        /* update cache */
438        /* XXX: check this - I'm not sure :( */
439        fat_fd->map.file_cln = start_cln +
440                               ((ofs_cln_save + cmpltd - 1) >> fs_info->vol.bpc_log2);
441        fat_fd->map.disk_cln = save_cln;
442    }
443
444    if (RC_OK != rc)
445      return rc;
446    else
447      return cmpltd;
448}
449
450/* fat_file_write --
451 *     Write 'count' bytes of data from user supplied buffer to fat-file
452 *     starting at offset 'start'. This interface hides the architecture
453 *     of fat-file, represents it as linear file
454 *
455 * PARAMETERS:
456 *     fs_info  - FS info
457 *     fat_fd   - fat-file descriptor
458 *     start    - offset(in bytes) to write from
459 *     count    - count
460 *     buf      - buffer provided by user
461 *
462 * RETURNS:
463 *     number of bytes actually written to the file on success, or -1 if
464 *     error occured (errno set appropriately)
465 */
466ssize_t
467fat_file_write(
468    fat_fs_info_t                        *fs_info,
469    fat_file_fd_t                        *fat_fd,
470    uint32_t                              start,
471    uint32_t                              count,
472    const uint8_t                        *buf
473    )
474{
475    int            rc = RC_OK;
476    ssize_t        ret;
477    uint32_t       cmpltd = 0;
478    uint32_t       byte;
479    uint32_t       c = 0;
480    bool           zero_fill = start > fat_fd->fat_file_size;
481    uint32_t       file_cln_initial = fat_fd->map.file_cln;
482    uint32_t       cln;
483
484
485    if ( count == 0 )
486        return cmpltd;
487
488    if (start >= fat_fd->size_limit)
489        rtems_set_errno_and_return_minus_one(EFBIG);
490
491    if (count > fat_fd->size_limit - start)
492        count = fat_fd->size_limit - start;
493
494    rc = fat_file_extend(fs_info, fat_fd, zero_fill, start + count, &c);
495    if (RC_OK == rc)
496    {
497        /*
498         * check whether there was enough room on device to locate
499         * file of 'start + count' bytes
500         */
501        if (c != (start + count))
502            count = c - start;
503
504        /* for the root directory of FAT12 and FAT16 we need this special handling */
505        if (fat_is_fat12_or_fat16_root_dir(fat_fd, fs_info->vol.type))
506        {
507            cln = fat_fd->cln;
508            cln += (start >> fs_info->vol.bpc_log2);
509            byte = start & (fs_info->vol.bpc -1);
510
511            ret = fat_cluster_write(fs_info,
512                                      cln,
513                                      byte,
514                                      count,
515                                      buf,
516                                      false);
517            if (0 > ret)
518              rc = -1;
519            else
520              cmpltd = ret;
521        }
522        else
523        {
524            ret = fat_file_write_fat32_or_non_root_dir(fs_info,
525                                                       fat_fd,
526                                                       start,
527                                                       count,
528                                                       buf,
529                                                       file_cln_initial);
530            if (0 > ret)
531              rc = -1;
532            else
533              cmpltd = ret;
534        }
535    }
536    if (RC_OK != rc)
537        return rc;
538    else
539        return cmpltd;
540}
541
542/* fat_file_extend --
543 *     Extend fat-file. If new length less than current fat-file size -
544 *     do nothing. Otherwise calculate necessary count of clusters to add,
545 *     allocate it and add new clusters chain to the end of
546 *     existing clusters chain.
547 *
548 * PARAMETERS:
549 *     fs_info    - FS info
550 *     fat_fd     - fat-file descriptor
551 *     new_length - new length
552 *     a_length   - placeholder for result - actual new length of file
553 *
554 * RETURNS:
555 *     RC_OK and new length of file on success, or -1 if error occured (errno
556 *     set appropriately)
557 */
558int
559fat_file_extend(
560    fat_fs_info_t                        *fs_info,
561    fat_file_fd_t                        *fat_fd,
562    bool                                  zero_fill,
563    uint32_t                              new_length,
564    uint32_t                             *a_length
565    )
566{
567    int            rc = RC_OK;
568    uint32_t       chain = 0;
569    uint32_t       bytes2add = 0;
570    uint32_t       cls2add = 0;
571    uint32_t       old_last_cl;
572    uint32_t       last_cl = 0;
573    uint32_t       bytes_remain = 0;
574    uint32_t       cls_added;
575    ssize_t        bytes_written;
576
577    *a_length = new_length;
578
579    if (new_length <= fat_fd->fat_file_size)
580        return RC_OK;
581
582    if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
583        (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
584        rtems_set_errno_and_return_minus_one( ENOSPC );
585
586    bytes_remain = (fs_info->vol.bpc -
587                   (fat_fd->fat_file_size & (fs_info->vol.bpc - 1))) &
588                   (fs_info->vol.bpc - 1);
589
590    bytes2add = new_length - fat_fd->fat_file_size;
591
592    if (bytes2add > bytes_remain)
593        bytes2add -= bytes_remain;
594    else
595        bytes2add = 0;
596
597    if (zero_fill && bytes_remain > 0) {
598        uint32_t start = fat_fd->fat_file_size;
599        uint32_t cl_start = start >> fs_info->vol.bpc_log2;
600        uint32_t ofs = start & (fs_info->vol.bpc - 1);
601        uint32_t cur_cln;
602
603        rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
604        if (rc != RC_OK)
605            return rc;
606
607        bytes_written = fat_cluster_set (fs_info, cur_cln, ofs, bytes_remain, 0);
608        if (bytes_remain != bytes_written)
609            return -1;
610    }
611
612    /*
613     * if in last cluster allocated for the file there is enough room to
614     * handle extention (hence we don't need to add even one cluster to the
615     * file ) - return
616     */
617    if (bytes2add == 0)
618        return RC_OK;
619
620    cls2add = ((bytes2add - 1) >> fs_info->vol.bpc_log2) + 1;
621
622    rc = fat_scan_fat_for_free_clusters(fs_info, &chain, cls2add,
623                                        &cls_added, &last_cl, zero_fill);
624
625    /* this means that low level I/O error occured */
626    if (rc != RC_OK)
627        return rc;
628
629    /* this means that no space left on device */
630    if ((cls_added == 0) && (bytes_remain == 0))
631        rtems_set_errno_and_return_minus_one(ENOSPC);
632
633    /*  check wether we satisfied request for 'cls2add' clusters */
634    if (cls2add != cls_added)
635    {
636        uint32_t missing = (cls2add - cls_added) << fs_info->vol.bpc_log2;
637
638        new_length -= bytes2add < missing ? bytes2add : missing;
639    }
640
641    if (cls_added > 0)
642    {
643        /* add new chain to the end of existing */
644        if ( fat_fd->fat_file_size == 0 )
645        {
646            fat_fd->map.disk_cln = fat_fd->cln = chain;
647            fat_fd->map.file_cln = 0;
648        }
649        else
650        {
651            if (fat_fd->map.last_cln != FAT_UNDEFINED_VALUE)
652            {
653                old_last_cl = fat_fd->map.last_cln;
654            }
655            else
656            {
657                rc = fat_file_ioctl(fs_info, fat_fd, F_CLU_NUM,
658                                    (fat_fd->fat_file_size - 1), &old_last_cl);
659                if ( rc != RC_OK )
660                {
661                    fat_free_fat_clusters_chain(fs_info, chain);
662                    return rc;
663                }
664            }
665
666            rc = fat_set_fat_cluster(fs_info, old_last_cl, chain);
667            if ( rc != RC_OK )
668            {
669                fat_free_fat_clusters_chain(fs_info, chain);
670                return rc;
671            }
672            fat_buf_release(fs_info);
673        }
674
675        /* update number of the last cluster of the file */
676        fat_fd->map.last_cln = last_cl;
677
678        if (fat_fd->fat_file_type == FAT_DIRECTORY)
679        {
680            rc = fat_init_clusters_chain(fs_info, chain);
681            if ( rc != RC_OK )
682            {
683                fat_free_fat_clusters_chain(fs_info, chain);
684                return rc;
685            }
686        }
687    }
688
689    *a_length = new_length;
690    fat_fd->fat_file_size = new_length;
691
692    return RC_OK;
693}
694
695/* fat_file_truncate --
696 *     Truncate fat-file. If new length greater than current fat-file size -
697 *     do nothing. Otherwise find first cluster to free and free all clusters
698 *     in the chain starting from this cluster.
699 *
700 * PARAMETERS:
701 *     fs_info    - FS info
702 *     fat_fd     - fat-file descriptor
703 *     new_length - new length
704 *
705 * RETURNS:
706 *     RC_OK on success, or -1 if error occured (errno set appropriately)
707 */
708int
709fat_file_truncate(
710    fat_fs_info_t                        *fs_info,
711    fat_file_fd_t                        *fat_fd,
712    uint32_t                              new_length
713    )
714{
715    int            rc = RC_OK;
716    uint32_t       cur_cln = 0;
717    uint32_t       cl_start = 0;
718    uint32_t       new_last_cln = FAT_UNDEFINED_VALUE;
719
720
721    if ( new_length >= fat_fd->fat_file_size )
722        return rc;
723
724    assert(fat_fd->fat_file_size);
725
726    cl_start = (new_length + fs_info->vol.bpc - 1) >> fs_info->vol.bpc_log2;
727
728    if ((cl_start << fs_info->vol.bpc_log2) >= fat_fd->fat_file_size)
729        return RC_OK;
730
731    if (cl_start != 0)
732    {
733        rc = fat_file_lseek(fs_info, fat_fd, cl_start - 1, &new_last_cln);
734        if (rc != RC_OK)
735            return rc;
736
737    }
738
739    rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
740    if (rc != RC_OK)
741        return rc;
742
743    rc = fat_free_fat_clusters_chain(fs_info, cur_cln);
744    if (rc != RC_OK)
745        return rc;
746
747    if (cl_start != 0)
748    {
749        rc = fat_set_fat_cluster(fs_info, new_last_cln, FAT_GENFAT_EOC);
750        if ( rc != RC_OK )
751            return rc;
752        fat_fd->map.file_cln = cl_start - 1;
753        fat_fd->map.disk_cln = new_last_cln;
754        fat_fd->map.last_cln = new_last_cln;
755    }
756    return RC_OK;
757}
758
759/* fat_file_ioctl --
760 *     F_CLU_NUM:
761 *         make mapping between serial number of the cluster in fat-file and
762 *         its real number on the volume
763 *
764 * PARAMETERS:
765 *     fat_fd     - fat-file descriptor
766 *     fs_info    - FS info
767 *     cmd        - command
768 *     ...
769 *
770 * RETURNS:
771 *     RC_OK on success, or -1 if error occured and errno set appropriately
772 */
773int
774fat_file_ioctl(
775    fat_fs_info_t                        *fs_info,
776    fat_file_fd_t                        *fat_fd,
777    int                                   cmd,
778    ...)
779{
780    int            rc = RC_OK;
781    uint32_t       cur_cln = 0;
782    uint32_t       cl_start = 0;
783    uint32_t       pos = 0;
784    uint32_t      *ret;
785    va_list        ap;
786
787    va_start(ap, cmd);
788
789    switch (cmd)
790    {
791        case F_CLU_NUM:
792            pos = va_arg(ap, uint32_t);
793            ret = va_arg(ap, uint32_t *);
794
795            /* sanity check */
796            if ( pos >= fat_fd->fat_file_size ) {
797                va_end(ap);
798                rtems_set_errno_and_return_minus_one( EIO );
799            }
800
801            if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
802                (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
803            {
804                /* cluster 0 (zero) reserved for root dir */
805                *ret  = 0;
806                rc = RC_OK;
807                break;
808            }
809
810            cl_start = pos >> fs_info->vol.bpc_log2;
811
812            rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
813            if ( rc != RC_OK )
814                break;
815
816            *ret = cur_cln;
817            break;
818
819        default:
820            errno = EINVAL;
821            rc = -1;
822            break;
823    }
824    va_end(ap);
825    return rc;
826}
827
828/* fat_file_mark_removed --
829 *     Remove the fat-file descriptor from "valid" hash table, insert it
830 *     into "removed-but-still-open" hash table and set up "removed" bit.
831 *
832 * PARAMETERS:
833 *     fat_fd     - fat-file descriptor
834 *     fs_info    - FS info
835 *
836 * RETURNS:
837 *     None
838 */
839void
840fat_file_mark_removed(
841    fat_fs_info_t                        *fs_info,
842    fat_file_fd_t                        *fat_fd
843    )
844{
845    uint32_t       key = 0;
846
847    key = fat_construct_key(fs_info, &fat_fd->dir_pos.sname);
848
849    _hash_delete(fs_info->vhash, key, fat_fd->ino, fat_fd);
850
851    _hash_insert(fs_info->rhash, key, fat_fd->ino, fat_fd);
852
853    fat_fd->flags |= FAT_FILE_REMOVED;
854}
855
856/* fat_file_size --
857 *     Calculate fat-file size - fat-file is nothing that clusters chain, so
858 *     go through all clusters in the chain and count it. Only
859 *     special case is root directory for FAT12/16 volumes.
860 *     This function is used only for directories which are fat-files with
861 *     non-zero length, hence 'fat_fd->cln' always contains valid data.
862 *     Calculated size is stored in 'fat_file_size' field of fat-file
863 *     descriptor.
864 *
865 * PARAMETERS:
866 *     fs_info  - FS info
867 *     fat_fd   - fat-file descriptor
868 *
869 * RETURNS:
870 *     RC_OK on success, or -1 if error occured (errno set appropriately)
871 */
872int
873fat_file_size(
874    fat_fs_info_t                        *fs_info,
875    fat_file_fd_t                        *fat_fd
876    )
877{
878    int            rc = RC_OK;
879    uint32_t       cur_cln = fat_fd->cln;
880    uint32_t       save_cln = 0;
881
882    /* Have we requested root dir size for FAT12/16? */
883    if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
884        (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
885    {
886        fat_fd->fat_file_size = fs_info->vol.rdir_size;
887        return rc;
888    }
889
890    fat_fd->fat_file_size = 0;
891
892    while ((cur_cln & fs_info->vol.mask) < fs_info->vol.eoc_val)
893    {
894        save_cln = cur_cln;
895        rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
896        if ( rc != RC_OK )
897            return rc;
898
899        fat_fd->fat_file_size += fs_info->vol.bpc;
900    }
901    fat_fd->map.last_cln = save_cln;
902    return rc;
903}
904
905/* hash support routines */
906
907/* _hash_insert --
908 *     Insert elemnt into hash based on key 'key1'
909 *
910 * PARAMETERS:
911 *     hash - hash element will be inserted into
912 *     key1 - key on which insertion is based on
913 *     key2 - not used during insertion
914 *     el   - element to insert
915 *
916 * RETURNS:
917 *     None
918 */
919static inline void
920_hash_insert(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
921             fat_file_fd_t *el)
922{
923    rtems_chain_append_unprotected((hash) + ((key1) % FAT_HASH_MODULE), &(el)->link);
924}
925
926
927/* _hash_delete --
928 *     Remove element from hash
929 *
930 * PARAMETERS:
931 *     hash - hash element will be removed from
932 *     key1 - not used
933 *     key2 - not used
934 *     el   - element to delete
935 *
936 * RETURNS:
937 *     None
938 */
939static inline void
940_hash_delete(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
941             fat_file_fd_t *el)
942{
943    rtems_chain_extract_unprotected(&(el)->link);
944}
945
946/* _hash_search --
947 *     Search element in hash. If both keys match pointer to found element
948 *     is returned
949 *
950 * PARAMETERS:
951 *     fs_info  - FS info
952 *     hash     - hash element will be removed from
953 *     key1     - search key
954 *     key2     - search key
955 *     ret      - placeholder for result
956 *
957 * RETURNS:
958 *     0 and pointer to found element on success, -1 otherwise
959 */
960static inline int
961_hash_search(
962    const fat_fs_info_t                   *fs_info,
963    rtems_chain_control                   *hash,
964    uint32_t                               key1,
965    uint32_t                               key2,
966    fat_file_fd_t                          **ret
967    )
968{
969    uint32_t          mod = (key1) % FAT_HASH_MODULE;
970    rtems_chain_node *the_node = rtems_chain_first(hash + mod);
971
972    for ( ; !rtems_chain_is_tail((hash) + mod, the_node) ; )
973    {
974        fat_file_fd_t *ffd = (fat_file_fd_t *)the_node;
975        uint32_t       ck =  fat_construct_key(fs_info, &ffd->dir_pos.sname);
976
977        if ( (key1) == ck)
978        {
979            if ( ((key2) == 0) || ((key2) == ffd->ino) )
980            {
981                *ret = (void *)the_node;
982                return 0;
983            }
984        }
985        the_node = the_node->next;
986    }
987    return -1;
988}
989
990static off_t
991fat_file_lseek(
992    fat_fs_info_t                         *fs_info,
993    fat_file_fd_t                         *fat_fd,
994    uint32_t                               file_cln,
995    uint32_t                              *disk_cln
996    )
997{
998    int rc = RC_OK;
999
1000    if (file_cln == fat_fd->map.file_cln)
1001        *disk_cln = fat_fd->map.disk_cln;
1002    else
1003    {
1004        uint32_t   cur_cln;
1005        uint32_t   count;
1006        uint32_t   i;
1007
1008        if (file_cln > fat_fd->map.file_cln)
1009        {
1010            cur_cln = fat_fd->map.disk_cln;
1011            count = file_cln - fat_fd->map.file_cln;
1012        }
1013        else
1014        {
1015            cur_cln = fat_fd->cln;
1016            count = file_cln;
1017        }
1018
1019        /* skip over the clusters */
1020        for (i = 0; i < count; i++)
1021        {
1022            rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
1023            if ( rc != RC_OK )
1024                return rc;
1025        }
1026
1027        /* update cache */
1028        fat_fd->map.file_cln = file_cln;
1029        fat_fd->map.disk_cln = cur_cln;
1030
1031        *disk_cln = cur_cln;
1032    }
1033    return RC_OK;
1034}
Note: See TracBrowser for help on using the repository browser.