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

4.115
Last change on this file since c735cd5 was cf4f962, checked in by Sebastian Huber <sebastian.huber@…>, on 10/23/14 at 06:21:26

dosfs: Write meta-data only if it changed

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