source: rtems/c/src/lib/libbsp/shared/umon/tfsDriver.c @ 3b7c123

4.115
Last change on this file since 3b7c123 was 3b7c123, checked in by Sebastian Huber <sebastian.huber@…>, on 03/13/12 at 10:33:51

Filesystem: Reference counting for locations

o A new data structure rtems_filesystem_global_location_t was

introduced to be used for

o the mount point location in the mount table entry,
o the file system root location in the mount table entry,
o the root directory location in the user environment, and
o the current directory location in the user environment.

During the path evaluation global start locations are obtained to
ensure that the current file system instance will be not unmounted in
the meantime.

o The user environment uses now reference counting and is protected

from concurrent access.

o The path evaluation process was completely rewritten and simplified.

The IMFS, RFS, NFS, and DOSFS use now a generic path evaluation
method. Recursive calls in the path evaluation have been replaced
with iteration to avoid stack overflows. Only the evaluation of
symbolic links is recursive. No dynamic memory allocations and
intermediate buffers are used in the high level path evaluation. No
global locks are held during the file system instance specific path
evaluation process.

o Recursive symbolic link evaluation is now limited by

RTEMS_FILESYSTEM_SYMLOOP_MAX. Applications can retrieve this value
via sysconf().

o The device file system (devFS) uses now no global variables and

allocation from the workspace. Node names are allocated from the
heap.

o The upper layer lseek() performs now some parameter checks.
o The upper layer ftruncate() performs now some parameter checks.
o unmask() is now restricted to the RWX flags and protected from

concurrent access.

o The fchmod_h and rmnod_h file system node handlers are now a file

system operation.

o The unlink_h operation has been removed. All nodes are now destroyed

with the rmnod_h operation.

o New lock_h, unlock_h, clonenod_h, and are_nodes_equal_h file system

operations.

o The path evaluation and file system operations are now protected by

per file system instance lock and unlock operations.

o Fix and test file descriptor duplicate in fcntl().
o New test fstests/fsnofs01.

  • Property mode set to 100644
File size: 15.4 KB
Line 
1/*
2 *  tfsDriver.c - MicroMonitor TFS Hookup to RTEMS FS
3 *
4 *  Initial release: Oct 1, 2004   by Ed Sutter
5 *
6 *  Modifications to support reference counting in the file system are
7 *  Copyright (c) 2012 embedded brains GmbH.
8 *
9 *  This code was derived from the tftpDriver.c code written by
10 *  W. Eric Norum, which was apparently derived from the IMFS driver.
11 *
12 *  This code was reformatted by Joel Sherrill from OAR Corporation and
13 *  Fernando Nicodemos <fgnicodemos@terra.com.br> from NCB - Sistemas
14 *  Embarcados Ltda. (Brazil) to be more compliant with RTEMS coding
15 *  standards and to eliminate C++ style comments.
16 *
17 *  The license and distribution terms for this file may be
18 *  found in the file LICENSE in this distribution or at
19 *  http://www.rtems.com/license/LICENSE.
20 *
21 *  $Id$
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28#include <errno.h>
29#include <fcntl.h>
30#include <inttypes.h>
31#include <rtems.h>
32#include <rtems/libio.h>
33#include <rtems/libio_.h>
34#include <rtems/seterr.h>
35#include <rtems/bspIo.h>
36#include <rtems/umon.h>
37
38#include <umon/tfs.h>
39#include <umon/monlib.h>
40
41#ifdef RTEMS_TFS_DRIVER_DEBUG
42#define RTEMS_TFS_DEBUG 1
43#else
44#define RTEMS_TFS_DEBUG 0
45#endif
46
47#define MAXFILESIZE 0x4000
48#define MAXTFDS  15
49
50/* Define these for thread safety...
51 */
52#ifndef newlib_tfdlock
53#define newlib_tfdlock()
54#endif
55
56#ifndef newlib_tfdunlock
57#define newlib_tfdunlock()
58#endif
59
60/* TFS file descriptor info:
61 */
62struct tfdinfo {
63  int   inuse;
64  int   tfd;
65  char *buf;
66  char  name[TFSNAMESIZE+1];
67  char  info[TFSNAMESIZE+1];
68} tfdtable[MAXTFDS];
69
70/*
71 * Pathname prefix
72 */
73char TFS_PATHNAME_PREFIX[128];
74
75static const rtems_filesystem_operations_table  rtems_tfs_ops;
76static const rtems_filesystem_file_handlers_r   rtems_tfs_handlers;
77
78static bool rtems_tfs_is_directory(
79    const char *path,
80    size_t pathlen
81)
82{
83    return path [pathlen - 1] == '/';
84}
85
86static int rtems_tfs_mount_me(
87  rtems_filesystem_mount_table_entry_t *mt_entry,
88  const void                           *data
89)
90{
91  char *root_path = strdup("/");
92
93  if (root_path == NULL) {
94    rtems_set_errno_and_return_minus_one(ENOMEM);
95  }
96
97  mt_entry->mt_fs_root->location.handlers = &rtems_tfs_handlers;
98  mt_entry->mt_fs_root->location.ops = &rtems_tfs_ops;
99  mt_entry->mt_fs_root->location.node_access = root_path;
100
101  return 0;
102}
103
104/* Initialize the TFS-RTEMS file system
105 */
106int rtems_initialize_tfs_filesystem(
107  const char *path
108)
109{
110  int status;
111
112  if (!path) {
113    printk( "TFS: No mount point specified\n" );
114    return -1;
115  }
116
117  strncpy( TFS_PATHNAME_PREFIX, path, sizeof(TFS_PATHNAME_PREFIX) );
118
119  status = mkdir( TFS_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO );
120  if ( status == -1 ) {
121    printk( "TFS: Unable to mkdir %s\n", TFS_PATHNAME_PREFIX );
122    return status;
123  }
124
125  if (rtems_filesystem_register( "tfs", rtems_tfs_mount_me ) < 0)
126    return -1;
127
128  status = mount( "umon", TFS_PATHNAME_PREFIX, "tfs", RTEMS_FILESYSTEM_READ_WRITE, NULL);
129
130  if (status < 0) {
131    printk( "TFS: Unable to mount on %s\n", TFS_PATHNAME_PREFIX );
132    perror("TFS mount failed");
133  }
134
135  return(status);
136}
137
138/*
139 * Convert a path to canonical form
140 */
141static void fixPath(char *path)
142{
143  char *inp, *outp, *base;
144
145  outp = inp = path;
146  base = NULL;
147  for (;;) {
148    if (inp[0] == '.') {
149      if (inp[1] == '\0')
150          break;
151      if (inp[1] == '/') {
152          inp += 2;
153          continue;
154      }
155      if (inp[1] == '.') {
156        if (inp[2] == '\0') {
157          if ((base != NULL) && (outp > base)) {
158            outp--;
159            while ((outp > base) && (outp[-1] != '/'))
160              outp--;
161          }
162          break;
163        }
164        if (inp[2] == '/') {
165          inp += 3;
166          if (base == NULL)
167            continue;
168          if (outp > base) {
169            outp--;
170            while ((outp > base) && (outp[-1] != '/'))
171              outp--;
172          }
173          continue;
174        }
175      }
176    }
177    if (base == NULL)
178      base = inp;
179    while (inp[0] != '/') {
180      if ((*outp++ = *inp++) == '\0')
181        return;
182    }
183    *outp++ = '/';
184    while (inp[0] == '/')
185      inp++;
186  }
187  *outp = '\0';
188}
189
190static void rtems_tfs_eval_path(rtems_filesystem_eval_path_context_t *self)
191{
192  int eval_flags = rtems_filesystem_eval_path_get_flags(self);
193
194  if ((eval_flags & RTEMS_LIBIO_MAKE) == 0) {
195    int rw = RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE;
196
197    if ((eval_flags & rw) != rw) {
198      rtems_filesystem_location_info_t *currentloc =
199        rtems_filesystem_eval_path_get_currentloc(self);
200      char *current = currentloc->node_access;
201      size_t currentlen = strlen(current);
202      const char *path = rtems_filesystem_eval_path_get_path(self);
203      size_t pathlen = rtems_filesystem_eval_path_get_pathlen(self);
204      size_t len = currentlen + pathlen;
205
206      rtems_filesystem_eval_path_clear_path(self);
207
208      current = realloc(current, len + 1);
209      if (current != NULL) {
210        memcpy(current + currentlen, path, pathlen);
211        current [len] = '\0';
212        if (!rtems_tfs_is_directory(current, len)) {
213          fixPath (current);
214        }
215        currentloc->node_access = current;
216      } else {
217        rtems_filesystem_eval_path_error(self, ENOMEM);
218      }
219    } else {
220      rtems_filesystem_eval_path_error(self, EINVAL);
221    }
222  } else {
223    rtems_filesystem_eval_path_error(self, EIO);
224  }
225}
226
227/*
228 * The routine which does most of the work for the IMFS open handler
229 * The full_path_name here is all text AFTER the TFS_PATHNAME_PREFIX
230 * string, so if the filename is "/TFS/abc", the full_path_name string
231 * is "abc"...
232 *
233 * Attempts to remap the incoming flags to TFS equivalent.
234 * Its not a perfect mapping, but gets pretty close.
235 * A comma-delimited path is supported to allow the user
236 * to specify TFS-stuff (flag string, info string, and a buffer).
237 * For example:
238 *  abc,e,script,0x400000
239 *  This is a file called "abc" that will have the TFS 'e' flag
240 *  and the TFS info field of "script".  The storage buffer is
241 *  supplied by the user at 0x400000.
242 */
243static int rtems_tfs_open_worker(
244  rtems_libio_t *iop,
245  char          *path,
246  int            oflag,
247  mode_t         mode
248)
249{
250  static int beenhere = 0;
251  long flagmode;
252  int  tfdidx, tfd;
253  struct tfdinfo *tip;
254  char *buf, *fstr, *istr, *bstr, pathcopy[TFSNAMESIZE*3+1];
255
256  if (RTEMS_TFS_DEBUG)
257    printk("_open_r(%s,0x%" PRIx32 ",0x%" PRIx32 ")\n",path,oflag,mode);
258
259  if (!beenhere) {
260    newlib_tfdlock();
261    for(tfdidx=0;tfdidx<MAXTFDS;tfdidx++)
262      tfdtable[tfdidx].inuse = 0;
263
264    tfdtable[0].inuse = 1;    /* fake entry for stdin */
265    tfdtable[1].inuse = 1;    /* fake entry for stdout */
266    tfdtable[2].inuse = 1;    /* fake entry for stderr */
267    newlib_tfdunlock();
268    beenhere = 1;
269  }
270
271  istr = fstr = bstr = buf = (char *)0;
272
273  /* Copy the incoming path to a local array so that we can safely
274   * modify the string...
275    */
276  if (strlen(path) > TFSNAMESIZE*3) {
277    return(ENAMETOOLONG);
278  }
279  strcpy(pathcopy,path);
280
281  /* The incoming string may have commas that are used to delimit the
282   * name from the TFS flag string, TFS info string and buffer.
283   * Check for the commas and test for maximum string length...
284   */
285  fstr = strchr(pathcopy,',');
286  if (fstr)  {
287    *fstr++ = 0;
288    istr = strchr(fstr,',');
289    if (istr) {
290      *istr++ = 0;
291      bstr = strchr(istr,',');
292      if (bstr)
293        *bstr++ = 0;
294    }
295  }
296  if (strlen(pathcopy) > TFSNAMESIZE) {
297    return(ENAMETOOLONG);
298  }
299  if (istr) {
300    if (strlen(istr) > TFSNAMESIZE) {
301      return(ENAMETOOLONG);
302    }
303  }
304
305  /* If O_EXCL and O_CREAT are set, then fail if the file exists...
306   */
307  if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
308    if (mon_tfsstat((char *)pathcopy)) {
309      return(EEXIST);
310    }
311  }
312
313  /* Only a few flag combinations are supported...
314   * O_RDONLY             Simple read-only
315   * O_WRONLY | O_APPEND       Each write starts at end of file
316   * O_WRONLY | O_TRUNC       If file exists, truncate it
317   * O_WRONLY | O_CREAT       Create if it doesn't exist
318   * O_WRONLY | O_CREAT | O_EXCL   Fail if file exists
319   */
320  switch(oflag & O_ACCMODE) {
321    case O_RDONLY:
322      flagmode = TFS_RDONLY;
323      break;
324    case O_WRONLY|O_APPEND:
325      flagmode = TFS_APPEND;
326      break;
327    case O_WRONLY|O_TRUNC:
328    case O_WRONLY|O_CREAT|O_TRUNC:
329      mon_tfsunlink((char *)pathcopy);
330      flagmode = TFS_CREATE|TFS_APPEND;
331      break;
332    case O_WRONLY|O_CREAT:
333    case O_WRONLY|O_CREAT|O_APPEND:
334      flagmode = TFS_CREATE|TFS_APPEND;
335      break;
336    case O_RDWR:
337    case O_WRONLY|O_CREAT|O_EXCL:
338      flagmode = TFS_CREATE|TFS_APPEND;
339      break;
340    default:
341      printk("_open_r(): flag 0x%i not supported\n",oflag);
342      return(ENOTSUP);
343  }
344
345  /* Find an open slot in our tfd table:
346   */
347  newlib_tfdlock();
348  for(tfdidx=0;tfdidx<MAXTFDS;tfdidx++) {
349    if (tfdtable[tfdidx].inuse == 0)
350      break;
351  }
352  if (tfdidx == MAXTFDS) {
353    newlib_tfdunlock();
354    return(EMFILE);
355  }
356  tip = &tfdtable[tfdidx];
357  tip->inuse = 1;
358  newlib_tfdunlock();
359
360  /* If file is opened for something other than O_RDONLY, then
361   * we need to allocate a buffer for the file..
362   * WARNING: It is the user's responsibility to make sure that
363   * the file size does not exceed this buffer.  Note that the
364   * buffer may be specified as part of the comma-delimited path.
365   */
366  if (flagmode == TFS_RDONLY) {
367    buf = (char *)0;
368  } else {
369    if (bstr)
370      buf = (char *)strtol(bstr,0,0);
371    else
372      buf = malloc(MAXFILESIZE);
373    if (!buf) {
374      newlib_tfdlock();
375      tip->inuse = 0;
376      newlib_tfdunlock();
377      return(ENOMEM);
378    }
379  }
380
381  /* Deal with tfs flags and tfs info fields if necessary:
382   */
383  if (fstr) {
384    long bflag;
385
386    bflag = mon_tfsctrl(TFS_FATOB,(long)fstr,0);
387    if (bflag == -1) {
388      return(EINVAL);
389    }
390    flagmode |= bflag;
391  }
392
393  if (istr)
394    strcpy(tip->info,istr);
395  else
396    tip->info[0] = 0;
397
398  tfd = mon_tfsopen((char *)pathcopy,flagmode,buf);
399  if (tfd >= 0) {
400    tip->tfd = tfd;
401    tip->buf = buf;
402    strcpy(tip->name,pathcopy);
403    iop->data0 = (uint32_t)tfdidx;
404    return(0);
405  } else {
406    printk("%s: %s\n",pathcopy,
407      (char *)mon_tfsctrl(TFS_ERRMSG,tfd,0));
408  }
409
410  if (buf)
411    free(buf);
412
413  newlib_tfdlock();
414  tip->inuse = 0;
415  newlib_tfdunlock();
416  return(EINVAL);
417}
418
419/*
420 * The IMFS open handler
421 */
422static int rtems_tfs_open(
423  rtems_libio_t *iop,
424  const char    *new_name,
425  int            oflag,
426  mode_t         mode
427)
428{
429  char *full_path_name;
430  int err;
431
432  full_path_name = iop->pathinfo.node_access;
433
434  if (RTEMS_TFS_DEBUG)
435    printk("rtems_tfs_open(%s)\n",full_path_name);
436
437  if (rtems_tfs_is_directory(full_path_name, strlen(full_path_name))) {
438    rtems_set_errno_and_return_minus_one (ENOTSUP);
439  }
440
441  err = rtems_tfs_open_worker (iop, full_path_name, oflag, mode);
442  if (err != 0) {
443     rtems_set_errno_and_return_minus_one (err);
444  }
445
446  return err;
447}
448
449/*
450 * Read from an open TFS file...
451 */
452static ssize_t rtems_tfs_read(
453  rtems_libio_t *iop,
454  void          *buffer,
455  uint32_t       count
456)
457{
458  int  ret, fd;
459
460  fd = (int) iop->data0;
461
462  if (RTEMS_TFS_DEBUG)
463    printk("_read_r(%d,%" PRId32 ")\n",fd,count);
464
465  if ((fd < 3) || (fd >= MAXTFDS))
466    return(EBADF);
467
468  ret = mon_tfsread(tfdtable[fd].tfd,buffer,count);
469  if (ret == TFSERR_EOF)
470    ret = 0;
471
472  return(ret);
473}
474
475/*
476 * Close the open tfs file.
477 */
478static int rtems_tfs_close(
479  rtems_libio_t *iop
480)
481{
482  int  fd;
483  char *info;
484  struct tfdinfo *tip;
485
486  fd = (int)iop->data0;
487
488  if (RTEMS_TFS_DEBUG)
489    printk("rtems_tfs_close(%d)\n",fd);
490
491  if ((fd < 3) || (fd >= MAXTFDS)) {
492    rtems_set_errno_and_return_minus_one (EBADF);
493  }
494
495  tip = &tfdtable[fd];
496
497  if (tip->info[0])
498    info = tip->info;
499  else
500    info = (char *)0;
501
502  mon_tfsclose(tip->tfd,info);
503
504  if (tip->buf)
505    free(tip->buf);
506
507  newlib_tfdlock();
508  tip->inuse = 0;
509  newlib_tfdunlock();
510  return RTEMS_SUCCESSFUL;
511}
512
513static ssize_t rtems_tfs_write(
514  rtems_libio_t *iop,
515  const void    *buffer,
516  uint32_t       count
517)
518{
519  int  ret, fd;
520
521  fd = (int) iop->data0;
522
523  if (RTEMS_TFS_DEBUG)
524    printk("rtems_tfs_write(%d,%" PRId32" )\n",fd,count);
525
526  if ((fd <= 0) || (fd >= MAXTFDS)) {
527    rtems_set_errno_and_return_minus_one (EBADF);
528  }
529
530  ret = mon_tfswrite(tfdtable[fd].tfd,(char *)buffer,count);
531  if (ret < 0)
532    return(-1);
533
534  return(ret);
535}
536
537static off_t rtems_tfs_lseek(
538  rtems_libio_t *iop,
539  off_t          offset,
540  int            whence
541)
542{
543  int ret, fd;
544
545  fd = (int) iop->data0;
546
547  if (RTEMS_TFS_DEBUG)
548    printk("rtems_tfs_lseek(%d,%ld,%d)\n",fd,(long)offset,whence);
549
550  switch (whence) {
551    case SEEK_END:
552      printk("rtems_tfs_lseek doesn't support SEEK_END\n");
553      return(-1);
554    case SEEK_CUR:
555      whence = TFS_CURRENT;
556      break;
557    case SEEK_SET:
558      whence = TFS_BEGIN;
559      break;
560  }
561  ret = mon_tfsseek(tfdtable[fd].tfd,offset,whence);
562
563  if (ret < 0)
564    return(-1);
565
566  return (off_t)ret;
567}
568
569/*
570 *
571 */
572static int rtems_tfs_ftruncate(
573  rtems_libio_t *iop,
574  off_t          count
575)
576{
577  int ret, fd;
578
579  fd = (int) iop->data0;
580  ret = mon_tfstruncate(tfdtable[fd].tfd,count);
581  if (ret != TFS_OKAY)
582    return(-1);
583
584  return(0);
585}
586
587static int rtems_tfs_ioctl(
588  rtems_libio_t *iop,
589  uint32_t       cmd,
590  void          *buf
591)
592{
593  int ret;
594
595  ret = mon_tfsctrl(cmd,(long)buf,0);
596  if (ret != TFS_OKAY)
597    return(-1);
598
599  return(0);
600}
601
602static rtems_filesystem_node_types_t rtems_tfs_node_type(
603  const rtems_filesystem_location_info_t *loc
604)
605{
606  const char *path = loc->node_access;
607  size_t pathlen = strlen(path);
608
609  return rtems_tfs_is_directory(path, pathlen) ?
610    RTEMS_FILESYSTEM_DIRECTORY
611      : RTEMS_FILESYSTEM_MEMORY_FILE;
612}
613
614static int rtems_tfs_clone_node_info(
615  rtems_filesystem_location_info_t *loc
616)
617{
618  int rv = 0;
619
620  loc->node_access = strdup(loc->node_access);
621
622  if (loc->node_access == NULL) {
623    errno = ENOMEM;
624    rv = -1;
625  }
626
627  return rv;
628}
629
630static void rtems_tfs_free_node_info(
631  const rtems_filesystem_location_info_t *loc
632)
633{
634  free(loc->node_access);
635}
636
637static bool rtems_tfs_are_nodes_equal(
638  const rtems_filesystem_location_info_t *a,
639  const rtems_filesystem_location_info_t *b
640)
641{
642  return strcmp(a->node_access, b->node_access) == 0;
643}
644
645static const rtems_filesystem_operations_table  rtems_tfs_ops = {
646  .lock_h = rtems_filesystem_default_lock,
647  .unlock_h = rtems_filesystem_default_unlock,
648  .eval_path_h = rtems_tfs_eval_path,
649  .link_h = rtems_filesystem_default_link,
650  .are_nodes_equal_h = rtems_tfs_are_nodes_equal,
651  .node_type_h = rtems_tfs_node_type,
652  .mknod_h = rtems_filesystem_default_mknod,
653  .rmnod_h = rtems_filesystem_default_rmnod,
654  .fchmod_h = rtems_filesystem_default_fchmod,
655  .chown_h = rtems_filesystem_default_chown,
656  .clonenod_h = rtems_tfs_clone_node_info,
657  .freenod_h = rtems_tfs_free_node_info,
658  .mount_h = rtems_filesystem_default_mount,
659  .fsmount_me_h = rtems_tfs_mount_me,
660  .unmount_h = rtems_filesystem_default_unmount,
661  .fsunmount_me_h = rtems_filesystem_default_fsunmount,
662  .utime_h = rtems_filesystem_default_utime,
663  .symlink_h = rtems_filesystem_default_symlink,
664  .readlink_h = rtems_filesystem_default_readlink,
665  .rename_h = rtems_filesystem_default_rename,
666  .statvfs_h = rtems_filesystem_default_statvfs
667};
668
669static const rtems_filesystem_file_handlers_r rtems_tfs_handlers = {
670  .open_h = rtems_tfs_open,
671  .close_h = rtems_tfs_close,
672  .read_h = rtems_tfs_read,
673  .write_h = rtems_tfs_write,
674  .ioctl_h = rtems_tfs_ioctl,
675  .lseek_h = rtems_tfs_lseek,
676  .fstat_h = rtems_filesystem_default_fstat,
677  .ftruncate_h = rtems_tfs_ftruncate,
678  .fsync_h = rtems_filesystem_default_fsync,
679  .fdatasync_h = rtems_filesystem_default_fdatasync,
680  .fcntl_h = rtems_filesystem_default_fcntl
681};
Note: See TracBrowser for help on using the repository browser.