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

5
Last change on this file since 0ec9bbc was 4d495cf, checked in by Sebastian Huber <sebastian.huber@…>, on 09/06/17 at 12:30:00

dosfs: Fix fat_file_update()

Do not update the non-existant meta-data of the root directory.

Update #2944.

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