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

4.115
Last change on this file since d883ce2 was d883ce2, checked in by Mathew Kallada <matkallada@…>, on 12/20/12 at 14:22:52

libfs: Doxygen Enhancement Task #6"

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