source: rtems/cpukit/libfs/src/rfs/rtems-rfs-file.c @ 8504f124

4.115
Last change on this file since 8504f124 was 8504f124, checked in by Chris Johns <chrisj@…>, on 12/09/11 at 07:21:02

2011-12-09 Chris Johns <chrisj@…>

PR 1968/filesystem

  • libfs/src/rfs/rtems-rfs-file.c: Fix to the seek bug where a seek to 0 after reading the end of the file did not point to the correct block.
  • libfs/src/rfs/rtems-rfs-rtems.h, libfs/src/rfs/rtems-rfs-trace.c: Fix the trace flags. Used to fix the bug.
  • Property mode set to 100644
File size: 18.5 KB
Line 
1/*
2 *  COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org>
3 *
4 *  The license and distribution terms for this file may be
5 *  found in the file LICENSE in this distribution or at
6 *  http://www.rtems.com/license/LICENSE.
7 *
8 *  $Id$
9 */
10/**
11 * @file
12 *
13 * @ingroup rtems-rfs
14 *
15 * RTEMS File Systems File Routines.
16 *
17 * These functions manage files.
18 */
19
20#if HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24#include <inttypes.h>
25
26#include <rtems/rfs/rtems-rfs-block-pos.h>
27#include <rtems/rfs/rtems-rfs-file.h>
28#include <rtems/rfs/rtems-rfs-file-system.h>
29#include <rtems/rfs/rtems-rfs-trace.h>
30
31int
32rtems_rfs_file_open (rtems_rfs_file_system*  fs,
33                     rtems_rfs_ino           ino,
34                     uint32_t                flags,
35                     rtems_rfs_file_handle** file)
36{
37  rtems_rfs_file_handle* handle;
38  rtems_rfs_file_shared* shared;
39  int                    rc;
40
41  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
42    printf ("rtems-rfs: file-open: ino=%" PRId32 "\n", ino);
43
44  *file = NULL;
45
46  /*
47   * Allocate a new handle and initialise it. Do this before we deal with the
48   * shared node data so we do not have to be concerned with reference
49   * counting.
50   */
51  handle = malloc (sizeof (rtems_rfs_file_handle));
52  if (!handle)
53    return ENOMEM;
54
55  memset (handle, 0, sizeof (rtems_rfs_file_handle));
56
57  rc = rtems_rfs_buffer_handle_open (fs, &handle->buffer);
58  if (rc > 0)
59  {
60    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
61      printf ("rtems-rfs: file-open: buffer handle open failed: %d: %s\n",
62              rc, strerror (rc));
63    free (handle);
64    return rc;
65  }
66
67  /*
68   * Scan the file system data list of open files for this ino. If found up
69   * the reference count and return the pointer to the data.
70   */
71  shared = rtems_rfs_file_get_shared (fs, ino);
72  if (shared)
73  {
74    shared->references++;
75    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
76      printf ("rtems-rfs: file-open: ino=%" PRId32 " shared\n", ino);
77  }
78  else
79  {
80    /*
81     * None exists so create. Copy in the shared parts of the inode we hold in
82     * memory.
83     */
84    shared = malloc (sizeof (rtems_rfs_file_shared));
85    if (!shared)
86    {
87      rtems_rfs_buffer_handle_close (fs, &handle->buffer);
88      free (handle);
89      return ENOMEM;
90    }
91
92    memset (shared, 0, sizeof (rtems_rfs_file_shared));
93
94    rc = rtems_rfs_inode_open (fs, ino, &shared->inode, true);
95    if (rc > 0)
96    {
97      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
98        printf ("rtems-rfs: file-open: inode open failed: %d: %s\n",
99                rc, strerror (rc));
100      free (shared);
101      rtems_rfs_buffer_handle_close (fs, &handle->buffer);
102      free (handle);
103      return rc;
104    }
105
106    rc = rtems_rfs_block_map_open (fs, &shared->inode, &shared->map);
107    if (rc > 0)
108    {
109      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
110        printf ("rtems-rfs: file-open: block map open failed: %d: %s\n",
111                rc, strerror (rc));
112      rtems_rfs_inode_close (fs, &shared->inode);
113      free (shared);
114      rtems_rfs_buffer_handle_close (fs, &handle->buffer);
115      free (handle);
116      return rc;
117    }
118
119    shared->references = 1;
120    shared->size.count = rtems_rfs_inode_get_block_count (&shared->inode);
121    shared->size.offset = rtems_rfs_inode_get_block_offset (&shared->inode);
122    shared->atime = rtems_rfs_inode_get_atime (&shared->inode);
123    shared->mtime = rtems_rfs_inode_get_mtime (&shared->inode);
124    shared->ctime = rtems_rfs_inode_get_ctime (&shared->inode);
125    shared->fs = fs;
126
127    rtems_chain_append (&fs->file_shares, &shared->link);
128
129    rtems_rfs_inode_unload (fs, &shared->inode, false);
130
131    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_OPEN))
132      printf ("rtems-rfs: file-open: ino=%" PRId32 " share created\n", ino);
133  }
134
135  handle->flags  = flags;
136  handle->shared = shared;
137
138  *file = handle;
139
140  return 0;
141}
142
143int
144rtems_rfs_file_close (rtems_rfs_file_system* fs,
145                      rtems_rfs_file_handle* handle)
146{
147  int rrc;
148  int rc;
149
150  rrc = 0;
151
152  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE))
153    printf ("rtems-rfs: file-close: entry: ino=%" PRId32 "\n",
154            handle->shared->inode.ino);
155
156  if (handle->shared->references > 0)
157    handle->shared->references--;
158
159  if (handle->shared->references == 0)
160  {
161    if (!rtems_rfs_inode_is_loaded (&handle->shared->inode))
162      rrc = rtems_rfs_inode_load (fs, &handle->shared->inode);
163
164    if (rrc == 0)
165    {
166      /*
167       * @todo This could be clever and only update if different.
168       */
169      rtems_rfs_inode_set_atime (&handle->shared->inode,
170                                 handle->shared->atime);
171      rtems_rfs_inode_set_mtime (&handle->shared->inode,
172                                 handle->shared->mtime);
173      rtems_rfs_inode_set_ctime (&handle->shared->inode,
174                                 handle->shared->ctime);
175      if (!rtems_rfs_block_size_equal (&handle->shared->size,
176                                       &handle->shared->map.size))
177        rtems_rfs_block_map_set_size (&handle->shared->map,
178                                      &handle->shared->size);
179    }
180
181    rc = rtems_rfs_block_map_close (fs, &handle->shared->map);
182    if (rc > 0)
183    {
184      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE))
185        printf ("rtems-rfs: file-close: map close error: ino=%" PRId32 ": %d: %s\n",
186                handle->shared->inode.ino, rc, strerror (rc));
187      if (rrc == 0)
188        rrc = rc;
189    }
190
191    rc = rtems_rfs_inode_close (fs, &handle->shared->inode);
192    if (rc > 0)
193    {
194      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE))
195        printf ("rtems-rfs: file-close: inode close error: ino=%" PRId32 ": %d: %s\n",
196                handle->shared->inode.ino, rc, strerror (rc));
197      if (rrc == 0)
198        rrc = rc;
199    }
200
201    rtems_chain_extract (&handle->shared->link);
202    free (handle->shared);
203  }
204
205  rc = rtems_rfs_buffer_handle_close (fs, &handle->buffer);
206  if ((rrc == 0) && (rc > 0))
207    rrc = rc;
208
209  if (rrc > 0)
210  {
211    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_CLOSE))
212      printf ("rtems-rfs: file-close: result: %d: %s\n", rrc, strerror (rrc));
213  }
214
215  free (handle);
216
217  return rrc;
218}
219
220int
221rtems_rfs_file_io_start (rtems_rfs_file_handle* handle,
222                         size_t*                available,
223                         bool                   read)
224{
225  size_t size;
226
227  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
228    printf ("rtems-rfs: file-io: start: %s pos=%" PRIu32 ":%" PRIu32 "\n",
229            read ? "read" : "write",  handle->bpos.bno, handle->bpos.boff);
230
231  if (!rtems_rfs_buffer_handle_has_block (&handle->buffer))
232  {
233    rtems_rfs_buffer_block block;
234    bool                   request_read;
235    int                    rc;
236
237    request_read = read;
238
239    rc = rtems_rfs_block_map_find (rtems_rfs_file_fs (handle),
240                                   rtems_rfs_file_map (handle),
241                                   rtems_rfs_file_bpos (handle),
242                                   &block);
243    if (rc > 0)
244    {
245      /*
246       * Has the read reached the EOF ?
247       */
248      if (read && (rc == ENXIO))
249      {
250        *available = 0;
251        return 0;
252      }
253
254      if (rc != ENXIO)
255        return rc;
256
257      if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
258        printf ("rtems-rfs: file-io: start: grow\n");
259
260      rc = rtems_rfs_block_map_grow (rtems_rfs_file_fs (handle),
261                                     rtems_rfs_file_map (handle),
262                                     1, &block);
263      if (rc > 0)
264        return rc;
265
266      request_read = false;
267    }
268    else
269    {
270      /*
271       * If this is a write check if the write starts within a block or the
272       * amount of data is less than a block size. If it is read the block
273       * rather than getting a block to fill.
274       */
275      if (!read &&
276          (rtems_rfs_file_block_offset (handle) ||
277           (*available < rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle)))))
278        request_read = true;
279    }
280
281    if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
282      printf ("rtems-rfs: file-io: start: block=%" PRIu32 " request-read=%s\n",
283              block, request_read ? "yes" : "no");
284
285    rc = rtems_rfs_buffer_handle_request (rtems_rfs_file_fs (handle),
286                                          rtems_rfs_file_buffer (handle),
287                                          block, request_read);
288    if (rc > 0)
289      return rc;
290  }
291
292  if (read
293      && rtems_rfs_block_map_last (rtems_rfs_file_map (handle))
294      && rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle)))
295    size = rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle));
296  else
297    size = rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle));
298
299  *available = size - rtems_rfs_file_block_offset (handle);
300
301  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
302    printf ("rtems-rfs: file-io: start: available=%zu (%zu)\n",
303            *available, size);
304
305  return 0;
306}
307
308int
309rtems_rfs_file_io_end (rtems_rfs_file_handle* handle,
310                       size_t                 size,
311                       bool                   read)
312{
313  bool atime;
314  bool mtime;
315  bool length;
316  int  rc = 0;
317
318  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
319    printf ("rtems-rfs: file-io:   end: %s size=%zu\n",
320            read ? "read" : "write", size);
321
322  if (rtems_rfs_buffer_handle_has_block (&handle->buffer))
323  {
324    if (!read)
325      rtems_rfs_buffer_mark_dirty (rtems_rfs_file_buffer (handle));
326    rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle),
327                                          rtems_rfs_file_buffer (handle));
328    if (rc > 0)
329    {
330      printf (
331        "rtems-rfs: file-io:   end: error on release: %s size=%zu: %d: %s\n",
332        read ? "read" : "write", size, rc, strerror (rc));
333
334      return rc;
335    }
336  }
337
338  /*
339   * Update the handle's position. Only a block size can be handled at a time
340   * so no special maths is needed. If the offset is bigger than the block size
341   * increase the block number and adjust the offset.
342   *
343   * If we are the last block and the position is past the current size update
344   * the size with the new length. The map holds the block count.
345   */
346  handle->bpos.boff += size;
347
348  if (handle->bpos.boff >=
349      rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle)))
350  {
351    handle->bpos.bno++;
352    handle->bpos.boff -= rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle));
353  }
354
355  length = false;
356  mtime = false;
357
358  if (!read &&
359      rtems_rfs_block_map_past_end (rtems_rfs_file_map (handle),
360                                    rtems_rfs_file_bpos (handle)))
361  {
362    rtems_rfs_block_map_set_size_offset (rtems_rfs_file_map (handle),
363                                         handle->bpos.boff);
364    length = true;
365    mtime = true;
366  }
367
368  atime  = rtems_rfs_file_update_atime (handle);
369  mtime  = rtems_rfs_file_update_mtime (handle) && mtime;
370  length = rtems_rfs_file_update_length (handle) && length;
371
372  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
373    printf ("rtems-rfs: file-io:   end: pos=%" PRIu32 ":%" PRIu32 " %c %c %c\n",
374            handle->bpos.bno, handle->bpos.boff,
375            atime ? 'A' : '-', mtime ? 'M' : '-', length ? 'L' : '-');
376
377  if (atime || mtime)
378  {
379    time_t now = time (NULL);
380    if (read && atime)
381      handle->shared->atime = now;
382    if (!read && mtime)
383      handle->shared->mtime = now;
384  }
385  if (length)
386  {
387    handle->shared->size.count =
388      rtems_rfs_block_map_count (rtems_rfs_file_map (handle));
389    handle->shared->size.offset =
390      rtems_rfs_block_map_size_offset (rtems_rfs_file_map (handle));
391  }
392
393  return rc;
394}
395
396int
397rtems_rfs_file_io_release (rtems_rfs_file_handle* handle)
398{
399  int rc = 0;
400  if (rtems_rfs_buffer_handle_has_block (&handle->buffer))
401    rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle),
402                                          rtems_rfs_file_buffer (handle));
403  return rc;
404}
405
406int
407rtems_rfs_file_seek (rtems_rfs_file_handle* handle,
408                     rtems_rfs_pos          pos,
409                     rtems_rfs_pos*         new_pos)
410{
411  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
412    printf ("rtems-rfs: file-seek: new=%" PRIu64 "\n", pos);
413
414  /*
415   * This call only sets the position if it is in a valid part of the file. The
416   * user can request past the end of the file then write to extend the
417   * file. The lseek entry states:
418   *
419   *    "Although lseek() may position the file offset beyond the end of the
420   *     file, this function does not itself extend the size of the file."
421   *
422   * This means the file needs to set the file size to the pos only when a
423   * write occurs.
424   */
425  if (pos < rtems_rfs_file_shared_get_size (rtems_rfs_file_fs (handle),
426                                            handle->shared))
427  {
428    rtems_rfs_file_set_bpos (handle, pos);
429   
430    /*
431     * If the file has a block check if it maps to the current position and it
432     * does not release it. That will force us to get the block at the new
433     * position when the I/O starts.
434     */
435    if (rtems_rfs_buffer_handle_has_block (&handle->buffer))
436    {
437      rtems_rfs_buffer_block block;
438      int                    rc;
439     
440      rc = rtems_rfs_block_map_find (rtems_rfs_file_fs (handle),
441                                     rtems_rfs_file_map (handle),
442                                     rtems_rfs_file_bpos (handle),
443                                     &block);
444      if (rc > 0)
445        return rc;
446      if (rtems_rfs_buffer_bnum (&handle->buffer) != block)
447      {       
448        rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle),
449                                              rtems_rfs_file_buffer (handle));
450        if (rc > 0)
451          return rc;
452      }
453    }
454  }
455  else
456  {
457    /*
458     * The seek is outside the current file so release any buffer. A write will
459     * extend the file.
460     */
461    int rc = rtems_rfs_file_io_release (handle);
462    if (rc > 0)
463      return rc;
464  }
465 
466  *new_pos = pos;
467  return 0;
468}
469
470int
471rtems_rfs_file_set_size (rtems_rfs_file_handle* handle,
472                         rtems_rfs_pos          new_size)
473{
474  rtems_rfs_block_map* map  = rtems_rfs_file_map (handle);
475  rtems_rfs_pos        size;
476  int                  rc;
477
478  if (rtems_rfs_trace (RTEMS_RFS_TRACE_FILE_IO))
479    printf ("rtems-rfs: file-set-size: size=%" PRIu64 "\n", new_size);
480
481  size = rtems_rfs_file_size (handle);
482 
483  /*
484   * If the file is same size do nothing else grow or shrink it ?
485   *
486   * If the file does not change size do not update the times.
487   */
488  if (size != new_size)
489  {
490    /*
491     * Short cut for the common truncate on open call.
492     */
493    if (new_size == 0)
494    {
495      rc = rtems_rfs_block_map_free_all (rtems_rfs_file_fs (handle), map);
496      if (rc > 0)
497        return rc;
498    }
499    else
500    {
501      if (size < new_size)
502      {
503        /*
504         * Grow. Fill with 0's.
505         */
506        rtems_rfs_pos count;
507        uint32_t      length;
508        bool          read_block;
509
510        count = new_size - size;
511        length = rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle));
512        read_block = false;
513
514        while (count)
515        {
516          rtems_rfs_buffer_block block;
517          rtems_rfs_block_pos    bpos;
518          uint8_t*               dst;
519
520          /*
521           * Get the block position for the current end of the file as seen by
522           * the map. If not found and the EOF grow the map then fill the block
523           * with 0.
524           */
525          rtems_rfs_block_size_get_bpos (rtems_rfs_block_map_size (map), &bpos);
526          rc = rtems_rfs_block_map_find (rtems_rfs_file_fs (handle),
527                                         map, &bpos, &block);
528          if (rc > 0)
529          {
530            /*
531             * Have we reached the EOF ?
532             */
533            if (rc != ENXIO)
534              return rc;
535
536            rc = rtems_rfs_block_map_grow (rtems_rfs_file_fs (handle),
537                                           map, 1, &block);
538            if (rc > 0)
539              return rc;
540          }
541
542          if (count < (length - bpos.boff))
543          {
544            length = count + bpos.boff;
545            read_block = true;
546            rtems_rfs_block_map_set_size_offset (map, length);
547          }
548          else
549          {
550            rtems_rfs_block_map_set_size_offset (map, 0);
551          }
552
553          /*
554           * Only read the block if the length is not the block size.
555           */
556          rc = rtems_rfs_buffer_handle_request (rtems_rfs_file_fs (handle),
557                                                rtems_rfs_file_buffer (handle),
558                                                block, read_block);
559          if (rc > 0)
560            return rc;
561
562          dst = rtems_rfs_buffer_data (&handle->buffer);
563          memset (dst + bpos.boff, 0, length - bpos.boff);
564
565          rtems_rfs_buffer_mark_dirty (rtems_rfs_file_buffer (handle));
566
567          rc = rtems_rfs_buffer_handle_release (rtems_rfs_file_fs (handle),
568                                                rtems_rfs_file_buffer (handle));
569          if (rc > 0)
570            return rc;
571
572          count -= length - bpos.boff;
573        }
574      }
575      else
576      {
577        /*
578         * Shrink
579         */
580        rtems_rfs_block_no blocks;
581        uint32_t           offset;
582
583        blocks =
584          rtems_rfs_block_map_count (map) -
585          (((new_size - 1) /
586            rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle))) + 1);
587
588        offset =
589          new_size % rtems_rfs_fs_block_size (rtems_rfs_file_fs (handle));
590
591        if (blocks)
592        {
593          int rc;
594          rc = rtems_rfs_block_map_shrink (rtems_rfs_file_fs (handle),
595                                           rtems_rfs_file_map (handle),
596                                           blocks);
597          if (rc > 0)
598            return rc;
599        }
600
601        rtems_rfs_block_map_set_size_offset (map, offset);
602
603        if (rtems_rfs_block_pos_past_end (rtems_rfs_file_bpos (handle),
604                                          rtems_rfs_block_map_size (map)))
605          rtems_rfs_block_size_get_bpos (rtems_rfs_block_map_size (map),
606                                         rtems_rfs_file_bpos (handle));
607      }
608    }
609
610    handle->shared->size.count  = rtems_rfs_block_map_count (map);
611    handle->shared->size.offset = rtems_rfs_block_map_size_offset (map);
612
613    if (rtems_rfs_file_update_mtime (handle))
614      handle->shared->mtime = time (NULL);
615  }
616 
617  return 0;
618}
619
620rtems_rfs_file_shared*
621rtems_rfs_file_get_shared (rtems_rfs_file_system* fs,
622                           rtems_rfs_ino          ino)
623{
624  rtems_chain_node* node;
625  node = rtems_chain_first (&fs->file_shares);
626  while (!rtems_chain_is_tail (&fs->file_shares, node))
627  {
628    rtems_rfs_file_shared* shared;
629    shared = (rtems_rfs_file_shared*) node;
630    if (shared->inode.ino == ino)
631      return shared;
632    node = rtems_chain_next (node);
633  }
634  return NULL;
635}
Note: See TracBrowser for help on using the repository browser.