static int zmr_parsefilename()

in system/zmodem/zm_receive.c [1127:1401]


static int zmr_parsefilename(FAR struct zmr_state_s *pzmr,
                             FAR const uint8_t *namptr)
{
  static uint32_t uniqno = 0;
  struct stat buf;
  uint8_t f0;
  uint8_t f1;
  bool exists;
  int ret;

  DEBUGASSERT(pzmr && !pzmr->filename);

  /* Don't allow absolute paths */

  if (*namptr == '/')
    {
      return -EINVAL;
    }

  /* Extend the relative path to the file storage directory */

  ret = asprintf(&pzmr->filename, "%s/%s", pzmr->pathname, namptr);
  if (ret < 0)
    {
      zmdbg("ERROR: Failed to allocate full path %s/%s\n",
            CONFIG_SYSTEM_ZMODEM_MOUNTPOINT, namptr);
      return -ENOMEM;
    }

  /* Check if the file already exists at this path */

  ret = stat(pzmr->filename, &buf);
  if (ret == OK)
    {
      exists = true;
    }
  else
    {
      int errcode = errno;
      if (errcode == ENOENT)
        {
          exists = false;
        }
      else
        {
          zmdbg("ERROR: stat of %s failed: %d\n", pzmr->filename, errcode);
          ret = -errcode;
          goto errout_with_filename;
        }
    }

  /* If remote end has not specified transfer flags (f0), we can use the
   * local definitions.  We will stick with f0=f1=0 which will be a binary
   * file.
   */

  f0 = pzmr->f0;
  f1 = pzmr->f1;

  zmdbg("File %s f0=%02x, f1=%-2x, exists=%d, size=%ld/%ld\n",
        pzmr->filename, f0, f1, exists, (long)buf.st_size,
        (long)pzmr->filesize);

  /* Are we appending to an existing file? */

  if (f0 == ZCRESUM)
    {
      /* One corner case:  The file exists and is already the requested
       * size.  We already have the file!
       */

      if (exists && buf.st_size == pzmr->filesize)
        {
          zmdbg("ZCRESUM: Rejected\n");
          ret = -EEXIST;
          goto errout_with_filename;
        }

      /* Otherwise, indicate that we will append to the file (whether it
       * exists yet or not.
       */

      pzmr->cmn.flags |= ZM_FLAG_APPEND;
    }

  /* Check if the first is required to be there and fail if it is not */

  if (!exists && (f1 & ZMSKNOLOC) != 0)
    {
      zmdbg("ZMSKNOLOC: Rejected\n");
      ret = -ENOENT;
      goto errout_with_filename;
    }

  /* Now perform whatever actions are required for this file */

  switch (f1 & ZMMASK)
    {
    case ZMNEWL:              /* Transfer if source newer or longer */
      {
#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS
        zmdbg("ZMNEWL: timestamp=%08lx st_mtime=%08lx\n",
               (unsigned long)pzmr->timestamp, (unsigned long)buf.st_mtime);
#endif
        zmdbg("ZMNEWL: filesize=%08lx st_size=%08lx\n",
               (unsigned long)pzmr->filesize, (unsigned long)buf.st_size);

        if (exists &&
#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS
            pzmr->timestamp <= buf.st_mtime &&
#endif
            pzmr->filesize <= buf.st_size)
          {
            zmdbg("ZMNEWL: Rejected\n");
            ret = -EPERM;
            goto errout_with_filename;
          }
      }
      break;

    case ZMCRC:               /* Transfer if different CRC or length */
      {
        uint32_t crc = zm_filecrc(&pzmr->cmn, pzmr->filename);
        zmdbg("ZMCRC: crc=%08x vs. %08x\n", pzmr->crc, crc);
        zmdbg("       filesize=%08lx st_size=%08lx\n",
               (unsigned long)pzmr->filesize, (unsigned long)buf.st_size);

        if (exists &&
            pzmr->filesize == buf.st_size &&
            pzmr->crc == crc)
          {
            zmdbg("ZMCRC: Rejected\n");
            ret = -EPERM;
            goto errout_with_filename;
          }
      }
      break;

    case ZMAPND:              /* Append to existing file, if any */
      pzmr->cmn.flags |= ZM_FLAG_APPEND;

    case 0:                   /* Implementation dependent */
    case ZMCLOB:              /* Replace existing file */
      break;

    case ZMNEW:               /* Transfer if source is newer */
      {
#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS
        zmdbg("ZMNEWL: timestamp=%08lx st_mtime=%08lx\n",
              (unsigned long)pzmr->timestamp, (unsigned long)buf.st_mtime);

        if (exists &&
            pzmr->timestamp <= buf.st_mtime)
          {
            zmdbg("ZMNEW: Rejected\n");
            ret = -EPERM;
            goto errout_with_filename;
          }
#endif
      }
      break;

    case ZMDIFF:              /* Transfer if dates or lengths different */
      {
#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS
        zmdbg("ZMDIFF: timestamp=%08lx st_mtime=%0l8x\n",
               (unsigned long)pzmr->timestamp, (unsigned long)buf.st_mtime);
#endif
        zmdbg("ZMDIFF: filesize=%08lx st_size=%08lx\n",
               (unsigned long)pzmr->filesize, (unsigned long)buf.st_size);

        if (exists &&
#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS
            pzmr->timestamp == buf.st_mtime &&
#endif
            pzmr->filesize == buf.st_size)
          {
            zmdbg("ZMDIFF: Rejected\n");
            ret = -EPERM;
            goto errout_with_filename;
          }
      }
      break;

    case ZMPROT:              /* Protect: transfer only if dest doesn't exist */
      {
        if (exists)
          {
            zmdbg("ZMPROT: Rejected\n");
            ret = -EPERM;
            goto errout_with_filename;
          }
      }
      break;

    case ZMCHNG:              /* Change filename if destination exists */
      {
        FAR char *candidate;

        /* Does the file exist? */

        if (exists)
          {
            /* Yes.. loop until we create a unique file name */

            while (exists)
              {
                /* Create a candidate file name */

                ret = asprintf(&candidate, "%s_%" PRId32, pzmr->filename,
                               ++uniqno);
                if (ret < 0)
                  {
                    zmdbg("ERROR:  Failed to allocate candidate %s_%d\n",
                          pzmr->filename, uniqno);
                    ret = -ENOMEM;
                    goto errout_with_filename;
                  }

                /* Does the candidate also exist? */

                ret = stat(candidate, &buf);
                if (ret < 0)
                  {
                    int errcode = errno;
                    if (errcode != ENOENT)
                      {
                        zmdbg("ERROR: stat of %s failed: %d\n", candidate,
                              errcode);
                      }

                    /* Free the old filename and replace it with the
                     * candidate
                     */

                    free(pzmr->filename);
                    pzmr->filename = candidate;
                    exists = false;
                  }
              }
          }
        }
        break;

      default:
        ret = -EINVAL;
        goto errout_with_filename;
    }

  /* We have accepted pzmr->filename.  If the file exists and we are not
   * appending to it, then unlink the old file now.
   */

  if (exists && (pzmr->cmn.flags & ZM_FLAG_APPEND) == 0)
    {
      ret = unlink(pzmr->filename);
      if (ret != OK)
        {
          int errorcode = errno;

          zmdbg("ERROR: unlink of %s failed: %d\n", pzmr->filename,
                errorcode);
          ret = -errorcode;
          goto errout_with_filename;
        }
    }

  zmdbg("Accepted filename: %s\n", pzmr->filename);
  return OK;

errout_with_filename:
  free(pzmr->filename);
  pzmr->filename = NULL;
  return ret;
}