Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:17

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSImplTFTPFS
0007  *
0008  * @brief This source file contains the implementation of
0009  *   the Trivial File Transfer Protocol (TFTP) file system.
0010  *
0011  * The code in this file handles the file system operations (such as
0012  * `mount()`, `open()`, `read()`, `write()`, `close()` etc.).
0013  * The networking part, i.e. the actual Trivial File Transfer Protocol
0014  * implementation, is realized in another file - the
0015  * @ref tftpDriver.c "TFTP client library".
0016  */
0017 
0018 /*
0019  * Copyright (C) 1998 W. Eric Norum <eric@norum.ca>
0020  * Copyright (C) 2012, 2022 embedded brains GmbH & Co. KG
0021  *
0022  * Redistribution and use in source and binary forms, with or without
0023  * modification, are permitted provided that the following conditions
0024  * are met:
0025  * 1. Redistributions of source code must retain the above copyright
0026  *    notice, this list of conditions and the following disclaimer.
0027  * 2. Redistributions in binary form must reproduce the above copyright
0028  *    notice, this list of conditions and the following disclaimer in the
0029  *    documentation and/or other materials provided with the distribution.
0030  *
0031  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0032  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0033  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0034  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0035  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0036  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0037  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0038  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0039  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0040  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0041  * POSSIBILITY OF SUCH DAMAGE.
0042  */
0043 
0044 #ifdef HAVE_CONFIG_H
0045 #include "config.h"
0046 #endif
0047 
0048 #include <stdio.h>
0049 #include <stdlib.h>
0050 #include <inttypes.h>
0051 #include <errno.h>
0052 #include <malloc.h>
0053 #include <string.h>
0054 #include <unistd.h>
0055 #include <fcntl.h>
0056 #include <rtems.h>
0057 #include <rtems/libio_.h>
0058 #include <rtems/seterr.h>
0059 #include <rtems/tftp.h>
0060 #include <rtems/thread.h>
0061 
0062 #include "tftp_driver.h"
0063 
0064 /*
0065  * Flags for filesystem info.
0066  */
0067 #define TFTPFS_VERBOSE (1 << 0)
0068 
0069 /*
0070  * TFTP File system info.
0071  */
0072 typedef struct tftpfs_info_s {
0073   uint32_t flags;
0074   rtems_mutex tftp_mutex;
0075   size_t nStreams;
0076   void ** volatile tftpStreams;
0077   tftp_net_config tftp_config;
0078 } tftpfs_info_t;
0079 
0080 #define tftpfs_info_mount_table(_mt) ((tftpfs_info_t*) ((_mt)->fs_info))
0081 #define tftpfs_info_pathloc(_pl)     ((tftpfs_info_t*) ((_pl)->mt_entry->fs_info))
0082 #define tftpfs_info_iop(_iop)        (tftpfs_info_pathloc (&((_iop)->pathinfo)))
0083 
0084 /* Forward declarations */
0085 static const rtems_filesystem_operations_table  rtems_tftp_ops;
0086 static const rtems_filesystem_file_handlers_r   rtems_tftp_handlers;
0087 
0088 static bool rtems_tftp_is_directory(
0089     const char *path,
0090     size_t pathlen
0091 )
0092 {
0093     return path [pathlen - 1] == '/';
0094 }
0095 
0096 /*
0097  * Return value:
0098  *   0   if options have been pracessed without error
0099  *   N+1 if parsing failed at position N
0100  */
0101 ssize_t _Tftpfs_Parse_options(
0102   const char *option_str,
0103   tftp_net_config *tftp_config,
0104   uint32_t *flags
0105 )
0106 {
0107   const char *cur_pos = option_str;
0108   size_t verbose_len = strlen ("verbose");
0109   size_t rfc1350_len = strlen ("rfc1350");
0110   int len;
0111 
0112   while(cur_pos != NULL && *cur_pos != '\0') {
0113     if (strncmp (cur_pos, "verbose", verbose_len) == 0) {
0114       *flags |= TFTPFS_VERBOSE;
0115       len = (int) verbose_len;
0116     } else if (strncmp (cur_pos, "rfc1350", rfc1350_len) == 0) {
0117       tftp_config->options.block_size = TFTP_RFC1350_BLOCK_SIZE;
0118       tftp_config->options.window_size = TFTP_RFC1350_WINDOW_SIZE;
0119       len = (int) rfc1350_len;
0120     } else if (sscanf(
0121         cur_pos,
0122         "blocksize=%"SCNu16"%n",
0123         &tftp_config->options.block_size,
0124         &len
0125       ) == 1) {
0126     } else if (sscanf(
0127         cur_pos,
0128         "windowsize=%"SCNu16"%n",
0129         &tftp_config->options.window_size,
0130         &len
0131       ) == 1) {
0132     } else if (*cur_pos == ',') { /* skip surplus "," */
0133       len = 0;
0134     } else {
0135       return cur_pos - option_str + 1;
0136     }
0137 
0138     cur_pos += len;
0139     if (*cur_pos != ',' && *cur_pos != '\0') {
0140       return cur_pos - option_str + 1;
0141     }
0142     if (*cur_pos == ',') {
0143       cur_pos++;
0144     }
0145   }
0146 
0147   return 0;
0148 }
0149 
0150 int rtems_tftpfs_initialize(
0151   rtems_filesystem_mount_table_entry_t *mt_entry,
0152   const void                           *data
0153 )
0154 {
0155   const char *device = mt_entry->dev;
0156   size_t devicelen = strlen (device);
0157   tftpfs_info_t *fs = NULL;
0158   char *root_path;
0159   size_t err_pos;
0160   int errno_store = ENOMEM;
0161 
0162   if (devicelen == 0) {
0163     root_path = malloc (1);
0164     if (root_path == NULL)
0165       goto error;
0166     root_path [0] = '\0';
0167   }
0168   else {
0169     root_path = malloc (devicelen + 2);
0170     if (root_path == NULL)
0171       goto error;
0172 
0173     root_path = memcpy (root_path, device, devicelen);
0174     root_path [devicelen] = '/';
0175     root_path [devicelen + 1] = '\0';
0176   }
0177 
0178   fs = malloc (sizeof (*fs));
0179   if (fs == NULL)
0180     goto error;
0181   fs->flags = 0;
0182   fs->nStreams = 0;
0183   fs->tftpStreams = 0;
0184 
0185   tftp_initialize_net_config (&fs->tftp_config);
0186   err_pos = _Tftpfs_Parse_options (data, &fs->tftp_config, &fs->flags);
0187   if (err_pos != 0) {
0188     printf(
0189       "TFTP FS: ERROR in mount options '%s'.\n"
0190         "TFTP FS: Cannot parse from this point: '%s'\n",
0191       ((char *) data),
0192       ((char *) data) + (err_pos - 1)
0193     );
0194     errno_store = EINVAL;
0195     goto error;
0196   }
0197 
0198   mt_entry->fs_info = fs;
0199   mt_entry->mt_fs_root->location.node_access = root_path;
0200   mt_entry->mt_fs_root->location.handlers = &rtems_tftp_handlers;
0201   mt_entry->ops = &rtems_tftp_ops;
0202 
0203   /*
0204    *  Now allocate a semaphore for mutual exclusion.
0205    *
0206    *  NOTE:  This could be in an fsinfo for this filesystem type.
0207    */
0208 
0209   rtems_mutex_init (&fs->tftp_mutex, "TFTPFS");
0210 
0211   return 0;
0212 
0213 error:
0214 
0215   free (fs);
0216   free (root_path);
0217 
0218   rtems_set_errno_and_return_minus_one (errno_store);
0219 }
0220 
0221 /*
0222  * Clear the pointer to a stream
0223  */
0224 static void
0225 releaseStream (tftpfs_info_t *fs, size_t s)
0226 {
0227     rtems_mutex_lock (&fs->tftp_mutex);
0228     fs->tftpStreams[s] = NULL;
0229     rtems_mutex_unlock (&fs->tftp_mutex);
0230 }
0231 
0232 static void
0233 rtems_tftpfs_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry)
0234 {
0235   tftpfs_info_t *fs = tftpfs_info_mount_table (mt_entry);
0236   size_t         s;
0237   void          *tp;
0238   for (s = 0; s < fs->nStreams; s++) {
0239       tp = fs->tftpStreams[s];
0240       releaseStream (fs, s);
0241       _Tftp_Destroy(tp);
0242   }
0243   rtems_mutex_destroy (&fs->tftp_mutex);
0244   free (fs);
0245   free (mt_entry->mt_fs_root->location.node_access);
0246 }
0247 
0248 /*
0249  * Convert a path to canonical form
0250  */
0251 static void
0252 fixPath (char *path)
0253 {
0254     char *inp, *outp, *base;
0255 
0256     outp = inp = path;
0257     base = NULL;
0258     for (;;) {
0259         if (inp[0] == '.') {
0260             if (inp[1] == '\0')
0261                 break;
0262             if (inp[1] == '/') {
0263                 inp += 2;
0264                 continue;
0265             }
0266             if (inp[1] == '.') {
0267                 if (inp[2] == '\0') {
0268                     if ((base != NULL) && (outp > base)) {
0269                         outp--;
0270                         while ((outp > base) && (outp[-1] != '/'))
0271                             outp--;
0272                     }
0273                     break;
0274                 }
0275                 if (inp[2] == '/') {
0276                     inp += 3;
0277                     if (base == NULL)
0278                         continue;
0279                     if (outp > base) {
0280                         outp--;
0281                         while ((outp > base) && (outp[-1] != '/'))
0282                             outp--;
0283                     }
0284                     continue;
0285                 }
0286             }
0287         }
0288         if (base == NULL)
0289             base = inp;
0290         while (inp[0] != '/') {
0291             if ((*outp++ = *inp++) == '\0')
0292                 return;
0293         }
0294         *outp++ = '/';
0295         while (inp[0] == '/')
0296             inp++;
0297     }
0298     *outp = '\0';
0299     return;
0300 }
0301 
0302 static void rtems_tftp_eval_path(rtems_filesystem_eval_path_context_t *self)
0303 {
0304     int eval_flags = rtems_filesystem_eval_path_get_flags (self);
0305 
0306     if ((eval_flags & RTEMS_FS_MAKE) == 0) {
0307         int rw = RTEMS_FS_PERMS_READ | RTEMS_FS_PERMS_WRITE;
0308 
0309         if ((eval_flags & rw) != rw) {
0310             rtems_filesystem_location_info_t *currentloc =
0311                 rtems_filesystem_eval_path_get_currentloc (self);
0312             char *current = currentloc->node_access;
0313             size_t currentlen = strlen (current);
0314             const char *path = rtems_filesystem_eval_path_get_path (self);
0315             size_t pathlen = rtems_filesystem_eval_path_get_pathlen (self);
0316             size_t len = currentlen + pathlen;
0317 
0318             rtems_filesystem_eval_path_clear_path (self);
0319 
0320             current = realloc (current, len + 1);
0321             if (current != NULL) {
0322                 memcpy (current + currentlen, path, pathlen);
0323                 current [len] = '\0';
0324                 if (!rtems_tftp_is_directory (current, len)) {
0325                     fixPath (current);
0326                 }
0327                 currentloc->node_access = current;
0328             } else {
0329                 rtems_filesystem_eval_path_error (self, ENOMEM);
0330             }
0331         } else {
0332             rtems_filesystem_eval_path_error (self, EINVAL);
0333         }
0334     } else {
0335         rtems_filesystem_eval_path_error (self, EIO);
0336     }
0337 }
0338 
0339 /*
0340  * The routine which does most of the work for the IMFS open handler
0341  */
0342 static int rtems_tftp_open_worker(
0343     rtems_libio_t *iop,
0344     char          *full_path_name,
0345     int            oflag
0346 )
0347 {
0348     tftpfs_info_t        *fs;
0349     void                 *tp;
0350     size_t               s;
0351     char                 *cp1;
0352     char                 *remoteFilename;
0353     char                 *hostname;
0354     int                  err;
0355 
0356     /*
0357      * Get the file system info.
0358      */
0359     fs = tftpfs_info_iop (iop);
0360 
0361     /*
0362      * Extract the host name component
0363      */
0364     if (*full_path_name == '/')
0365       full_path_name++;
0366 
0367     hostname = full_path_name;
0368     cp1 = strchr (full_path_name, ':');
0369     if (!cp1) {
0370         return EINVAL; /* No ':' in path: no hostname or no filename */
0371     } else {
0372         *cp1 = '\0';
0373         ++cp1;
0374     }
0375 
0376     /*
0377      * Extract file pathname component
0378      */
0379     if (*cp1 == '\0')
0380         return ENOENT;
0381     remoteFilename = cp1;
0382 
0383     /*
0384      * Establish the connection
0385      */
0386     err = tftp_open (
0387         hostname,
0388         remoteFilename,
0389         (oflag & O_ACCMODE) == O_RDONLY,
0390         &fs->tftp_config,
0391         &tp
0392     );
0393     if (err != 0) {
0394       return err;
0395     }
0396 
0397     /*
0398      * Find a free stream
0399      */
0400     rtems_mutex_lock (&fs->tftp_mutex);
0401     for (s = 0 ; s < fs->nStreams ; s++) {
0402         if (fs->tftpStreams[s] == NULL)
0403         break;
0404     }
0405     if (s == fs->nStreams) {
0406         /*
0407          * Reallocate stream pointers
0408          * Guard against the case where realloc() returns NULL.
0409          */
0410         void **np;
0411 
0412         np = realloc (fs->tftpStreams, ++fs->nStreams * sizeof *fs->tftpStreams);
0413         if (np == NULL) {
0414             rtems_mutex_unlock (&fs->tftp_mutex);
0415             tftp_close( tp );
0416             return ENOMEM;
0417         }
0418         fs->tftpStreams = np;
0419     }
0420     fs->tftpStreams[s] = tp;
0421     rtems_mutex_unlock (&fs->tftp_mutex);
0422     iop->data0 = s;
0423     iop->data1 = tp;
0424 
0425     return 0;
0426 }
0427 
0428 static int rtems_tftp_open(
0429     rtems_libio_t *iop,
0430     const char    *new_name,
0431     int            oflag,
0432     mode_t         mode
0433 )
0434 {
0435     tftpfs_info_t *fs;
0436     char          *full_path_name;
0437     int           err;
0438 
0439     full_path_name = iop->pathinfo.node_access;
0440 
0441     if (rtems_tftp_is_directory (full_path_name, strlen (full_path_name))) {
0442         rtems_set_errno_and_return_minus_one (ENOTSUP);
0443     }
0444 
0445     /*
0446      * Get the file system info.
0447      */
0448     fs = tftpfs_info_iop (iop);
0449 
0450     if (fs->flags & TFTPFS_VERBOSE)
0451       printf ("TFTPFS: %s\n", full_path_name);
0452 
0453     err = rtems_tftp_open_worker (iop, full_path_name, oflag);
0454     if (err != 0) {
0455        rtems_set_errno_and_return_minus_one (err);
0456     }
0457 
0458     return 0;
0459 }
0460 
0461 /*
0462  * Read from a TFTP stream
0463  */
0464 static ssize_t rtems_tftp_read(
0465     rtems_libio_t *iop,
0466     void          *buffer,
0467     size_t         count
0468 )
0469 {
0470     void *tp = iop->data1;
0471     ssize_t result = tftp_read (tp, buffer, count);
0472 
0473     if (result < 0) {
0474         rtems_set_errno_and_return_minus_one (-result);
0475     }
0476     return result;
0477 }
0478 
0479 /*
0480  * Close a TFTP stream
0481  */
0482 static int rtems_tftp_close(
0483     rtems_libio_t *iop
0484 )
0485 {
0486     tftpfs_info_t     *fs;
0487     void              *tp = iop->data1;
0488     int                e = 0;
0489 
0490     /*
0491      * Get the file system info.
0492      */
0493     fs = tftpfs_info_iop (iop);
0494 
0495     if (!tp)
0496         rtems_set_errno_and_return_minus_one (EIO);
0497 
0498     releaseStream (fs, iop->data0);
0499     e = tftp_close (tp);
0500     if (e)
0501         rtems_set_errno_and_return_minus_one (e);
0502     return 0;
0503 }
0504 
0505 static ssize_t rtems_tftp_write(
0506     rtems_libio_t   *iop,
0507     const void      *buffer,
0508     size_t           count
0509 )
0510 {
0511     void *tp = iop->data1;
0512     ssize_t result = tftp_write (tp, buffer, count);
0513 
0514     if (result < 0) {
0515         rtems_set_errno_and_return_minus_one (-result);
0516     }
0517     return result;
0518 }
0519 
0520 /*
0521  * Dummy version to let fopen(xxxx,"w") work properly.
0522  */
0523 static int rtems_tftp_ftruncate(
0524     rtems_libio_t   *iop RTEMS_UNUSED,
0525     off_t            count RTEMS_UNUSED
0526 )
0527 {
0528     return 0;
0529 }
0530 
0531 static int rtems_tftp_fstat(
0532     const rtems_filesystem_location_info_t *loc,
0533     struct stat                            *buf
0534 )
0535 {
0536     const char *path = loc->node_access;
0537     size_t pathlen = strlen (path);
0538 
0539     buf->st_mode = S_IRWXU | S_IRWXG | S_IRWXO
0540         | (rtems_tftp_is_directory (path, pathlen) ? S_IFDIR : S_IFREG);
0541 
0542     return 0;
0543 }
0544 
0545 static int rtems_tftp_clone(
0546     rtems_filesystem_location_info_t *loc
0547 )
0548 {
0549     int rv = 0;
0550 
0551     loc->node_access = strdup (loc->node_access);
0552 
0553     if (loc->node_access == NULL) {
0554         errno = ENOMEM;
0555         rv = -1;
0556     }
0557 
0558     return rv;
0559 }
0560 
0561 static void rtems_tftp_free_node_info(
0562     const rtems_filesystem_location_info_t *loc
0563 )
0564 {
0565     free (loc->node_access);
0566 }
0567 
0568 static bool rtems_tftp_are_nodes_equal(
0569   const rtems_filesystem_location_info_t *a,
0570   const rtems_filesystem_location_info_t *b
0571 )
0572 {
0573   return strcmp (a->node_access, b->node_access) == 0;
0574 }
0575 
0576 static const rtems_filesystem_operations_table  rtems_tftp_ops = {
0577     .lock_h = rtems_filesystem_default_lock,
0578     .unlock_h = rtems_filesystem_default_unlock,
0579     .eval_path_h = rtems_tftp_eval_path,
0580     .link_h = rtems_filesystem_default_link,
0581     .are_nodes_equal_h = rtems_tftp_are_nodes_equal,
0582     .mknod_h = rtems_filesystem_default_mknod,
0583     .rmnod_h = rtems_filesystem_default_rmnod,
0584     .fchmod_h = rtems_filesystem_default_fchmod,
0585     .chown_h = rtems_filesystem_default_chown,
0586     .clonenod_h = rtems_tftp_clone,
0587     .freenod_h = rtems_tftp_free_node_info,
0588     .mount_h = rtems_filesystem_default_mount,
0589     .unmount_h = rtems_filesystem_default_unmount,
0590     .fsunmount_me_h = rtems_tftpfs_shutdown,
0591     .utimens_h = rtems_filesystem_default_utimens,
0592     .symlink_h = rtems_filesystem_default_symlink,
0593     .readlink_h = rtems_filesystem_default_readlink,
0594     .rename_h = rtems_filesystem_default_rename,
0595     .statvfs_h = rtems_filesystem_default_statvfs
0596 };
0597 
0598 static const rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
0599    .open_h = rtems_tftp_open,
0600    .close_h = rtems_tftp_close,
0601    .read_h = rtems_tftp_read,
0602    .write_h = rtems_tftp_write,
0603    .ioctl_h = rtems_filesystem_default_ioctl,
0604    .lseek_h = rtems_filesystem_default_lseek,
0605    .fstat_h = rtems_tftp_fstat,
0606    .ftruncate_h = rtems_tftp_ftruncate,
0607    .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
0608    .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
0609    .fcntl_h = rtems_filesystem_default_fcntl,
0610    .kqfilter_h = rtems_filesystem_default_kqfilter,
0611    .mmap_h = rtems_filesystem_default_mmap,
0612    .poll_h = rtems_filesystem_default_poll,
0613    .readv_h = rtems_filesystem_default_readv,
0614    .writev_h = rtems_filesystem_default_writev
0615 };