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

4.115
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

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