/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @brief MicroMonitor TFS Hookup to RTEMS FS */ /* * Initial release: Oct 1, 2004 by Ed Sutter * * Modifications to support reference counting in the file system are * Copyright (c) 2012 embedded brains GmbH. * * This code was derived from the tftpDriver.c code written by * W. Eric Norum, which was apparently derived from the IMFS driver. * * This code was reformatted by Joel Sherrill from OAR Corporation and * Fernando Nicodemos from NCB - Sistemas * Embarcados Ltda. (Brazil) to be more compliant with RTEMS coding * standards and to eliminate C++ style comments. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RTEMS_TFS_DRIVER_DEBUG #define RTEMS_TFS_DEBUG 1 #else #define RTEMS_TFS_DEBUG 0 #endif #define MAXFILESIZE 0x4000 #define MAXTFDS 15 /* Define these for thread safety... */ #ifndef newlib_tfdlock #define newlib_tfdlock() #endif #ifndef newlib_tfdunlock #define newlib_tfdunlock() #endif /* TFS file descriptor info: */ struct tfdinfo { int inuse; int tfd; char *buf; char name[TFSNAMESIZE+1]; char info[TFSNAMESIZE+1]; } tfdtable[MAXTFDS]; /* * Pathname prefix */ char TFS_PATHNAME_PREFIX[128]; static const rtems_filesystem_operations_table rtems_tfs_ops; static const rtems_filesystem_file_handlers_r rtems_tfs_handlers; static bool rtems_tfs_is_directory( const char *path, size_t pathlen ) { return path [pathlen - 1] == '/'; } static int rtems_tfs_mount_me( rtems_filesystem_mount_table_entry_t *mt_entry, const void *data ) { char *root_path = strdup("/"); if (root_path == NULL) { rtems_set_errno_and_return_minus_one(ENOMEM); } mt_entry->ops = &rtems_tfs_ops; mt_entry->mt_fs_root->location.handlers = &rtems_tfs_handlers; mt_entry->mt_fs_root->location.node_access = root_path; return 0; } /* Initialize the TFS-RTEMS file system */ int rtems_initialize_tfs_filesystem( const char *path ) { int status; if (!path) { printk( "TFS: No mount point specified\n" ); return -1; } strncpy( TFS_PATHNAME_PREFIX, path, sizeof(TFS_PATHNAME_PREFIX) ); status = mkdir( TFS_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO ); if ( status == -1 ) { printk( "TFS: Unable to mkdir %s\n", TFS_PATHNAME_PREFIX ); return status; } if (rtems_filesystem_register( "tfs", rtems_tfs_mount_me ) < 0) return -1; status = mount( "umon", TFS_PATHNAME_PREFIX, "tfs", RTEMS_FILESYSTEM_READ_WRITE, NULL); if (status < 0) { printk( "TFS: Unable to mount on %s\n", TFS_PATHNAME_PREFIX ); perror("TFS mount failed"); } return(status); } /* * Convert a path to canonical form */ static void fixPath(char *path) { char *inp, *outp, *base; outp = inp = path; base = NULL; for (;;) { if (inp[0] == '.') { if (inp[1] == '\0') break; if (inp[1] == '/') { inp += 2; continue; } if (inp[1] == '.') { if (inp[2] == '\0') { if ((base != NULL) && (outp > base)) { outp--; while ((outp > base) && (outp[-1] != '/')) outp--; } break; } if (inp[2] == '/') { inp += 3; if (base == NULL) continue; if (outp > base) { outp--; while ((outp > base) && (outp[-1] != '/')) outp--; } continue; } } } if (base == NULL) base = inp; while (inp[0] != '/') { if ((*outp++ = *inp++) == '\0') return; } *outp++ = '/'; while (inp[0] == '/') inp++; } *outp = '\0'; } static void rtems_tfs_eval_path(rtems_filesystem_eval_path_context_t *self) { int eval_flags = rtems_filesystem_eval_path_get_flags(self); if ((eval_flags & RTEMS_FS_MAKE) == 0) { int rw = RTEMS_FS_PERMS_READ | RTEMS_FS_PERMS_WRITE; if ((eval_flags & rw) != rw) { rtems_filesystem_location_info_t *currentloc = rtems_filesystem_eval_path_get_currentloc(self); char *current = currentloc->node_access; size_t currentlen = strlen(current); const char *path = rtems_filesystem_eval_path_get_path(self); size_t pathlen = rtems_filesystem_eval_path_get_pathlen(self); size_t len = currentlen + pathlen; rtems_filesystem_eval_path_clear_path(self); current = realloc(current, len + 1); if (current != NULL) { memcpy(current + currentlen, path, pathlen); current [len] = '\0'; if (!rtems_tfs_is_directory(current, len)) { fixPath (current); } currentloc->node_access = current; } else { rtems_filesystem_eval_path_error(self, ENOMEM); } } else { rtems_filesystem_eval_path_error(self, EINVAL); } } else { rtems_filesystem_eval_path_error(self, EIO); } } /* * The routine which does most of the work for the IMFS open handler * The full_path_name here is all text AFTER the TFS_PATHNAME_PREFIX * string, so if the filename is "/TFS/abc", the full_path_name string * is "abc"... * * Attempts to remap the incoming flags to TFS equivalent. * Its not a perfect mapping, but gets pretty close. * A comma-delimited path is supported to allow the user * to specify TFS-stuff (flag string, info string, and a buffer). * For example: * abc,e,script,0x400000 * This is a file called "abc" that will have the TFS 'e' flag * and the TFS info field of "script". The storage buffer is * supplied by the user at 0x400000. */ static int rtems_tfs_open_worker( rtems_libio_t *iop, char *path, int oflag, mode_t mode ) { static int beenhere = 0; long flagmode; int tfdidx, tfd; struct tfdinfo *tip; char *buf, *fstr, *istr, *bstr, pathcopy[TFSNAMESIZE*3+1]; if (RTEMS_TFS_DEBUG) printk("_open_r(%s,0x%x,0x%" PRIx32 ")\n",path,oflag,mode); if (!beenhere) { newlib_tfdlock(); for(tfdidx=0;tfdidx TFSNAMESIZE*3) { return(ENAMETOOLONG); } strcpy(pathcopy,path); /* The incoming string may have commas that are used to delimit the * name from the TFS flag string, TFS info string and buffer. * Check for the commas and test for maximum string length... */ fstr = strchr(pathcopy,','); if (fstr) { *fstr++ = 0; istr = strchr(fstr,','); if (istr) { *istr++ = 0; bstr = strchr(istr,','); if (bstr) *bstr++ = 0; } } if (strlen(pathcopy) > TFSNAMESIZE) { return(ENAMETOOLONG); } if (istr) { if (strlen(istr) > TFSNAMESIZE) { return(ENAMETOOLONG); } } /* If O_EXCL and O_CREAT are set, then fail if the file exists... */ if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { if (mon_tfsstat((char *)pathcopy)) { return(EEXIST); } } /* Only a few flag combinations are supported... * O_RDONLY Simple read-only * O_WRONLY | O_APPEND Each write starts at end of file * O_WRONLY | O_TRUNC If file exists, truncate it * O_WRONLY | O_CREAT Create if it doesn't exist * O_WRONLY | O_CREAT | O_EXCL Fail if file exists */ switch(oflag & O_ACCMODE) { case O_RDONLY: flagmode = TFS_RDONLY; break; case O_WRONLY|O_APPEND: flagmode = TFS_APPEND; break; case O_WRONLY|O_TRUNC: case O_WRONLY|O_CREAT|O_TRUNC: mon_tfsunlink((char *)pathcopy); flagmode = TFS_CREATE|TFS_APPEND; break; case O_WRONLY|O_CREAT: case O_WRONLY|O_CREAT|O_APPEND: flagmode = TFS_CREATE|TFS_APPEND; break; case O_RDWR: case O_WRONLY|O_CREAT|O_EXCL: flagmode = TFS_CREATE|TFS_APPEND; break; default: printk("_open_r(): flag 0x%i not supported\n",oflag); return(ENOTSUP); } /* Find an open slot in our tfd table: */ newlib_tfdlock(); for(tfdidx=0;tfdidxinuse = 1; newlib_tfdunlock(); /* If file is opened for something other than O_RDONLY, then * we need to allocate a buffer for the file.. * WARNING: It is the user's responsibility to make sure that * the file size does not exceed this buffer. Note that the * buffer may be specified as part of the comma-delimited path. */ if (flagmode == TFS_RDONLY) { buf = (char *)0; } else { if (bstr) buf = (char *)strtol(bstr,0,0); else buf = malloc(MAXFILESIZE); if (!buf) { newlib_tfdlock(); tip->inuse = 0; newlib_tfdunlock(); return(ENOMEM); } } /* Deal with tfs flags and tfs info fields if necessary: */ if (fstr) { long bflag; bflag = mon_tfsctrl(TFS_FATOB,(long)fstr,0); if (bflag == -1) { return(EINVAL); } flagmode |= bflag; } if (istr) strcpy(tip->info,istr); else tip->info[0] = 0; tfd = mon_tfsopen((char *)pathcopy,flagmode,buf); if (tfd >= 0) { tip->tfd = tfd; tip->buf = buf; strcpy(tip->name,pathcopy); iop->data0 = (uint32_t)tfdidx; return(0); } else { printk("%s: %s\n",pathcopy, (char *)mon_tfsctrl(TFS_ERRMSG,tfd,0)); } if (buf) free(buf); newlib_tfdlock(); tip->inuse = 0; newlib_tfdunlock(); return(EINVAL); } /* * The IMFS open handler */ static int rtems_tfs_open( rtems_libio_t *iop, const char *new_name, int oflag, mode_t mode ) { char *full_path_name; int err; full_path_name = iop->pathinfo.node_access; if (RTEMS_TFS_DEBUG) printk("rtems_tfs_open(%s)\n",full_path_name); if (rtems_tfs_is_directory(full_path_name, strlen(full_path_name))) { rtems_set_errno_and_return_minus_one (ENOTSUP); } err = rtems_tfs_open_worker (iop, full_path_name, oflag, mode); if (err != 0) { rtems_set_errno_and_return_minus_one (err); } return err; } /* * Read from an open TFS file... */ static ssize_t rtems_tfs_read( rtems_libio_t *iop, void *buffer, size_t count ) { int ret, fd; fd = (int) iop->data0; if (RTEMS_TFS_DEBUG) printk("_read_r(%d,%zi)\n",fd,count); if ((fd < 3) || (fd >= MAXTFDS)) return(EBADF); ret = mon_tfsread(tfdtable[fd].tfd,buffer,count); if (ret == TFSERR_EOF) ret = 0; return(ret); } /* * Close the open tfs file. */ static int rtems_tfs_close( rtems_libio_t *iop ) { int fd; char *info; struct tfdinfo *tip; fd = (int)iop->data0; if (RTEMS_TFS_DEBUG) printk("rtems_tfs_close(%d)\n",fd); if ((fd < 3) || (fd >= MAXTFDS)) { rtems_set_errno_and_return_minus_one (EBADF); } tip = &tfdtable[fd]; if (tip->info[0]) info = tip->info; else info = (char *)0; mon_tfsclose(tip->tfd,info); if (tip->buf) free(tip->buf); newlib_tfdlock(); tip->inuse = 0; newlib_tfdunlock(); return RTEMS_SUCCESSFUL; } static ssize_t rtems_tfs_write( rtems_libio_t *iop, const void *buffer, size_t count ) { int ret, fd; fd = (int) iop->data0; if (RTEMS_TFS_DEBUG) printk("rtems_tfs_write(%d,%zi)\n",fd,count); if ((fd <= 0) || (fd >= MAXTFDS)) { rtems_set_errno_and_return_minus_one (EBADF); } ret = mon_tfswrite(tfdtable[fd].tfd,(char *)buffer,count); if (ret < 0) return(-1); return(ret); } static off_t rtems_tfs_lseek( rtems_libio_t *iop, off_t offset, int whence ) { int ret, fd; fd = (int) iop->data0; if (RTEMS_TFS_DEBUG) printk("rtems_tfs_lseek(%d,%ld,%d)\n",fd,(long)offset,whence); switch (whence) { case SEEK_END: printk("rtems_tfs_lseek doesn't support SEEK_END\n"); return(-1); case SEEK_CUR: whence = TFS_CURRENT; break; case SEEK_SET: whence = TFS_BEGIN; break; } ret = mon_tfsseek(tfdtable[fd].tfd,offset,whence); if (ret < 0) return(-1); return (off_t)ret; } /* * */ static int rtems_tfs_ftruncate( rtems_libio_t *iop, off_t count ) { int ret, fd; fd = (int) iop->data0; ret = mon_tfstruncate(tfdtable[fd].tfd,count); if (ret != TFS_OKAY) return(-1); return(0); } static int rtems_tfs_ioctl( rtems_libio_t *iop, uint32_t cmd, void *buf ) { int ret; ret = mon_tfsctrl(cmd,(long)buf,0); if (ret != TFS_OKAY) return(-1); return(0); } static int rtems_tfs_fstat( const rtems_filesystem_location_info_t *loc, struct stat *buf ) { const char *path = loc->node_access; size_t pathlen = strlen(path); buf->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | (rtems_tfs_is_directory(path, pathlen) ? S_IFDIR : S_IFREG); return 0; } static int rtems_tfs_clone_node_info( rtems_filesystem_location_info_t *loc ) { int rv = 0; loc->node_access = strdup(loc->node_access); if (loc->node_access == NULL) { errno = ENOMEM; rv = -1; } return rv; } static void rtems_tfs_free_node_info( const rtems_filesystem_location_info_t *loc ) { free(loc->node_access); } static bool rtems_tfs_are_nodes_equal( const rtems_filesystem_location_info_t *a, const rtems_filesystem_location_info_t *b ) { return strcmp(a->node_access, b->node_access) == 0; } static const rtems_filesystem_operations_table rtems_tfs_ops = { .lock_h = rtems_filesystem_default_lock, .unlock_h = rtems_filesystem_default_unlock, .eval_path_h = rtems_tfs_eval_path, .link_h = rtems_filesystem_default_link, .are_nodes_equal_h = rtems_tfs_are_nodes_equal, .mknod_h = rtems_filesystem_default_mknod, .rmnod_h = rtems_filesystem_default_rmnod, .fchmod_h = rtems_filesystem_default_fchmod, .chown_h = rtems_filesystem_default_chown, .clonenod_h = rtems_tfs_clone_node_info, .freenod_h = rtems_tfs_free_node_info, .mount_h = rtems_filesystem_default_mount, .unmount_h = rtems_filesystem_default_unmount, .fsunmount_me_h = rtems_filesystem_default_fsunmount, .utimens_h = rtems_filesystem_default_utimens, .symlink_h = rtems_filesystem_default_symlink, .readlink_h = rtems_filesystem_default_readlink, .rename_h = rtems_filesystem_default_rename, .statvfs_h = rtems_filesystem_default_statvfs }; static const rtems_filesystem_file_handlers_r rtems_tfs_handlers = { .open_h = rtems_tfs_open, .close_h = rtems_tfs_close, .read_h = rtems_tfs_read, .write_h = rtems_tfs_write, .ioctl_h = rtems_tfs_ioctl, .lseek_h = rtems_tfs_lseek, .fstat_h = rtems_tfs_fstat, .ftruncate_h = rtems_tfs_ftruncate, .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, .fcntl_h = rtems_filesystem_default_fcntl, .kqfilter_h = rtems_filesystem_default_kqfilter, .poll_h = rtems_filesystem_default_poll, .readv_h = rtems_filesystem_default_readv, .writev_h = rtems_filesystem_default_writev };