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;
}