657 lines
16 KiB
C
657 lines
16 KiB
C
/****************************************************************************
|
|
* fs/zipfs/zip_vfs.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statfs.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/fs/ioctl.h>
|
|
|
|
#include <unzip.h>
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct zipfs_dir_s
|
|
{
|
|
struct fs_dirent_s base;
|
|
mutex_t lock;
|
|
unzFile uf;
|
|
bool last;
|
|
};
|
|
|
|
struct zipfs_mountpt_s
|
|
{
|
|
char abspath[1];
|
|
};
|
|
|
|
struct zipfs_file_s
|
|
{
|
|
unzFile uf;
|
|
mutex_t lock;
|
|
FAR char *seekbuf;
|
|
char relpath[1];
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static voidpf zipfs_real_open(voidpf opaque, FAR const void *filename,
|
|
int mode);
|
|
static uLong zipfs_real_read(voidpf opaque, voidpf stream, FAR void *buf,
|
|
uLong size);
|
|
static long zipfs_real_seek(voidpf opaque, voidpf stream, ZPOS64_T offset,
|
|
int origin);
|
|
static ZPOS64_T zipfs_real_tell(voidpf opaque, voidpf stream);
|
|
static int zipfs_real_close(voidpf opaque, voidpf stream);
|
|
static int zipfs_real_error(voidpf opaque, voidpf stream);
|
|
|
|
static int zipfs_open(FAR struct file *filep, FAR const char *relpath,
|
|
int oflags, mode_t mode);
|
|
static int zipfs_close(FAR struct file *filep);
|
|
static ssize_t zipfs_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static off_t zipfs_seek(FAR struct file *filep, off_t offset,
|
|
int whence);
|
|
static int zipfs_dup(FAR const struct file *oldp,
|
|
FAR struct file *newp);
|
|
static int zipfs_fstat(FAR const struct file *filep,
|
|
FAR struct stat *buf);
|
|
static int zipfs_opendir(FAR struct inode *mountpt,
|
|
FAR const char *relpath,
|
|
FAR struct fs_dirent_s **dir);
|
|
static int zipfs_closedir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir);
|
|
static int zipfs_readdir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir,
|
|
FAR struct dirent *entry);
|
|
static int zipfs_rewinddir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir);
|
|
static int zipfs_bind(FAR struct inode *driver,
|
|
FAR const void *data, FAR void **handle);
|
|
static int zipfs_unbind(FAR void *handle, FAR struct inode **driver,
|
|
unsigned int flags);
|
|
static int zipfs_statfs(FAR struct inode *mountpt,
|
|
FAR struct statfs *buf);
|
|
static int zipfs_stat(FAR struct inode *mountpt,
|
|
FAR const char *relpath, FAR struct stat *buf);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static zlib_filefunc64_def zipfs_real_ops =
|
|
{
|
|
zipfs_real_open,
|
|
zipfs_real_read,
|
|
NULL,
|
|
zipfs_real_tell,
|
|
zipfs_real_seek,
|
|
zipfs_real_close,
|
|
zipfs_real_error,
|
|
NULL
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
const struct mountpt_operations g_zipfs_operations =
|
|
{
|
|
zipfs_open, /* open */
|
|
zipfs_close, /* close */
|
|
zipfs_read, /* read */
|
|
NULL, /* write */
|
|
zipfs_seek, /* seek */
|
|
NULL, /* ioctl */
|
|
NULL, /* mmap */
|
|
NULL, /* truncate */
|
|
NULL, /* poll */
|
|
|
|
NULL, /* sync */
|
|
zipfs_dup, /* dup */
|
|
zipfs_fstat, /* fstat */
|
|
NULL, /* fchstat */
|
|
|
|
zipfs_opendir, /* opendir */
|
|
zipfs_closedir, /* closedir */
|
|
zipfs_readdir, /* readdir */
|
|
zipfs_rewinddir, /* rewinddir */
|
|
|
|
zipfs_bind, /* bind */
|
|
zipfs_unbind, /* unbind */
|
|
zipfs_statfs, /* statfs */
|
|
|
|
NULL, /* unlink */
|
|
NULL, /* mkdir */
|
|
NULL, /* rmdir */
|
|
NULL, /* rename */
|
|
zipfs_stat, /* stat */
|
|
NULL /* chstat */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static voidpf zipfs_real_open(voidpf opaque, FAR const void *filename,
|
|
int mode)
|
|
{
|
|
FAR struct file *filep;
|
|
int ret;
|
|
|
|
filep = kmm_malloc(sizeof(struct file));
|
|
if (filep == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = file_open(filep, filename, O_RDONLY);
|
|
if (ret < 0)
|
|
{
|
|
kmm_free(filep);
|
|
return NULL;
|
|
}
|
|
|
|
return filep;
|
|
}
|
|
|
|
static uLong zipfs_real_read(voidpf opaque, voidpf stream,
|
|
FAR void *buf, uLong size)
|
|
{
|
|
return file_read(stream, buf, size);
|
|
}
|
|
|
|
static ZPOS64_T zipfs_real_tell(voidpf opaque, voidpf stream)
|
|
{
|
|
return file_seek(stream, 0, SEEK_CUR);
|
|
}
|
|
|
|
static long zipfs_real_seek(voidpf opaque, voidpf stream, ZPOS64_T offset,
|
|
int origin)
|
|
{
|
|
int ret;
|
|
|
|
ret = file_seek(stream, offset, origin);
|
|
return ret >= 0 ? 0 : ret;
|
|
}
|
|
|
|
static int zipfs_real_close(voidpf opaque, voidpf stream)
|
|
{
|
|
int ret;
|
|
|
|
ret = file_close(stream);
|
|
kmm_free(stream);
|
|
return ret;
|
|
}
|
|
|
|
static int zipfs_real_error(voidpf opaque, voidpf stream)
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
static int zipfs_convert_result(int ziperr)
|
|
{
|
|
switch (ziperr)
|
|
{
|
|
case UNZ_END_OF_LIST_OF_FILE:
|
|
return -ENOENT;
|
|
case UNZ_CRCERROR:
|
|
return -ESTALE;
|
|
case UNZ_INTERNALERROR:
|
|
return -EPERM;
|
|
case UNZ_BADZIPFILE:
|
|
return -EBADF;
|
|
case UNZ_PARAMERROR:
|
|
return -EINVAL;
|
|
default:
|
|
return ziperr;
|
|
}
|
|
}
|
|
|
|
static int zipfs_open(FAR struct file *filep, FAR const char *relpath,
|
|
int oflags, mode_t mode)
|
|
{
|
|
FAR struct zipfs_mountpt_s *fs = filep->f_inode->i_private;
|
|
FAR struct zipfs_file_s *fp;
|
|
int ret;
|
|
|
|
DEBUGASSERT(fs != NULL);
|
|
|
|
fp = kmm_malloc(sizeof(*fp) + strlen(relpath));
|
|
if (fp == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = nxmutex_init(&fp->lock);
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_fp;
|
|
}
|
|
|
|
fp->uf = unzOpen2_64(fs->abspath, &zipfs_real_ops);
|
|
if (fp->uf == NULL)
|
|
{
|
|
ret = -EINVAL;
|
|
goto err_with_mutex;
|
|
}
|
|
|
|
ret = zipfs_convert_result(unzLocateFile(fp->uf, relpath, 0));
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_zip;
|
|
}
|
|
|
|
ret = zipfs_convert_result(unzOpenCurrentFile(fp->uf));
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_zip;
|
|
}
|
|
|
|
if (ret == OK)
|
|
{
|
|
fp->seekbuf = NULL;
|
|
strcpy(fp->relpath, relpath);
|
|
filep->f_priv = fp;
|
|
}
|
|
else
|
|
{
|
|
err_with_zip:
|
|
unzClose(fp->uf);
|
|
err_with_mutex:
|
|
nxmutex_destroy(&fp->lock);
|
|
err_with_fp:
|
|
kmm_free(fp);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int zipfs_close(FAR struct file *filep)
|
|
{
|
|
FAR struct zipfs_file_s *fp = filep->f_priv;
|
|
int ret;
|
|
|
|
ret = zipfs_convert_result(unzClose(fp->uf));
|
|
nxmutex_destroy(&fp->lock);
|
|
kmm_free(fp->seekbuf);
|
|
kmm_free(fp);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t zipfs_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct zipfs_file_s *fp = filep->f_priv;
|
|
ssize_t ret;
|
|
|
|
nxmutex_lock(&fp->lock);
|
|
ret = zipfs_convert_result(unzReadCurrentFile(fp->uf, buffer, buflen));
|
|
if (ret > 0)
|
|
{
|
|
filep->f_pos += ret;
|
|
}
|
|
|
|
nxmutex_unlock(&fp->lock);
|
|
return ret;
|
|
}
|
|
|
|
static off_t zipfs_skip(FAR struct zipfs_file_s *fp, off_t amount)
|
|
{
|
|
off_t next = 0;
|
|
|
|
if (fp->seekbuf == NULL)
|
|
{
|
|
fp->seekbuf = kmm_malloc(CONFIG_ZIPFS_SEEK_BUFSIZE);
|
|
if (fp->seekbuf == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
while (next < amount)
|
|
{
|
|
off_t remain = amount - next;
|
|
|
|
if (remain > CONFIG_ZIPFS_SEEK_BUFSIZE)
|
|
{
|
|
remain = CONFIG_ZIPFS_SEEK_BUFSIZE;
|
|
}
|
|
|
|
remain = unzReadCurrentFile(fp->uf, fp->seekbuf, remain);
|
|
remain = zipfs_convert_result(remain);
|
|
if (remain <= 0)
|
|
{
|
|
return next ? next : remain;
|
|
}
|
|
|
|
next += remain;
|
|
}
|
|
|
|
return next;
|
|
}
|
|
|
|
static off_t zipfs_seek(FAR struct file *filep, off_t offset,
|
|
int whence)
|
|
{
|
|
FAR struct zipfs_mountpt_s *fs = filep->f_inode->i_private;
|
|
FAR struct zipfs_file_s *fp = filep->f_priv;
|
|
unz_file_info64 file_info;
|
|
off_t ret = 0;
|
|
|
|
nxmutex_lock(&fp->lock);
|
|
switch (whence)
|
|
{
|
|
case SEEK_SET:
|
|
break;
|
|
case SEEK_CUR:
|
|
offset += filep->f_pos;
|
|
break;
|
|
case SEEK_END:
|
|
ret = unzGetCurrentFileInfo64(fp->uf, &file_info,
|
|
NULL, 0, NULL, 0, NULL, 0);
|
|
ret = zipfs_convert_result(ret);
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_lock;
|
|
}
|
|
|
|
offset += file_info.uncompressed_size;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto err_with_lock;
|
|
}
|
|
|
|
if (filep->f_pos == offset)
|
|
{
|
|
goto err_with_lock;
|
|
}
|
|
else if (filep->f_pos > offset)
|
|
{
|
|
ret = zipfs_convert_result(unzClose(fp->uf));
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_lock;
|
|
}
|
|
|
|
fp->uf = unzOpen2_64(fs->abspath, &zipfs_real_ops);
|
|
if (fp->uf == NULL)
|
|
{
|
|
ret = -EINVAL;
|
|
goto err_with_lock;
|
|
}
|
|
|
|
ret = zipfs_convert_result(unzLocateFile(fp->uf, fp->relpath, 0));
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_lock;
|
|
}
|
|
|
|
ret = zipfs_convert_result(unzOpenCurrentFile(fp->uf));
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_lock;
|
|
}
|
|
|
|
filep->f_pos = 0;
|
|
}
|
|
|
|
ret = zipfs_skip(fp, offset - filep->f_pos);
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_lock;
|
|
}
|
|
|
|
if (ret >= 0)
|
|
{
|
|
filep->f_pos += ret;
|
|
}
|
|
|
|
err_with_lock:
|
|
nxmutex_unlock(&fp->lock);
|
|
return ret < 0 ? ret : filep->f_pos;
|
|
}
|
|
|
|
static int zipfs_dup(FAR const struct file *oldp, FAR struct file *newp)
|
|
{
|
|
FAR struct zipfs_file_s *fp;
|
|
|
|
fp = oldp->f_priv;
|
|
return zipfs_open(newp, fp->relpath, oldp->f_oflags, 0);
|
|
}
|
|
|
|
static int zipfs_stat_common(unzFile uf, FAR struct stat *buf)
|
|
{
|
|
unz_file_info64 file_info;
|
|
int ret;
|
|
|
|
memset(buf, 0, sizeof(struct stat));
|
|
|
|
ret = unzGetCurrentFileInfo64(uf, &file_info, NULL, 0,
|
|
NULL, 0, NULL, 0);
|
|
ret = zipfs_convert_result(ret);
|
|
if (ret >= 0)
|
|
{
|
|
buf->st_size = file_info.uncompressed_size;
|
|
buf->st_mode = S_IFREG | 0444;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int zipfs_fstat(FAR const struct file *filep,
|
|
FAR struct stat *buf)
|
|
{
|
|
FAR struct zipfs_file_s *fp = filep->f_priv;
|
|
|
|
return zipfs_stat_common(fp->uf, buf);
|
|
}
|
|
|
|
static int zipfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
|
|
FAR struct fs_dirent_s **dir)
|
|
{
|
|
FAR struct zipfs_mountpt_s *fs = mountpt->i_private;
|
|
FAR struct zipfs_dir_s *zdir;
|
|
int ret;
|
|
|
|
DEBUGASSERT(fs != NULL);
|
|
|
|
zdir = kmm_malloc(sizeof(*zdir));
|
|
if (zdir == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = nxmutex_init(&zdir->lock);
|
|
if (ret < 0)
|
|
{
|
|
kmm_free(zdir);
|
|
return ret;
|
|
}
|
|
|
|
zdir->uf = unzOpen2_64(fs->abspath, &zipfs_real_ops);
|
|
if (zdir->uf == NULL)
|
|
{
|
|
nxmutex_destroy(&zdir->lock);
|
|
kmm_free(zdir);
|
|
return -EINVAL;
|
|
}
|
|
|
|
zdir->last = false;
|
|
*dir = &zdir->base;
|
|
return ret;
|
|
}
|
|
|
|
static int zipfs_closedir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct zipfs_dir_s *zdir = (FAR struct zipfs_dir_s *)dir;
|
|
int ret;
|
|
|
|
zdir = (FAR struct zipfs_dir_s *)dir;
|
|
ret = zipfs_convert_result(unzClose(zdir->uf));
|
|
nxmutex_destroy(&zdir->lock);
|
|
kmm_free(zdir);
|
|
return ret;
|
|
}
|
|
|
|
static int zipfs_readdir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir,
|
|
FAR struct dirent *entry)
|
|
{
|
|
FAR struct zipfs_dir_s *zdir = (FAR struct zipfs_dir_s *)dir;
|
|
unz_file_info64 file_info;
|
|
int ret;
|
|
|
|
nxmutex_lock(&zdir->lock);
|
|
ret = unzGetCurrentFileInfo64(zdir->uf,
|
|
&file_info,
|
|
entry->d_name,
|
|
NAME_MAX, NULL, 0, NULL, 0);
|
|
|
|
ret = zipfs_convert_result(ret);
|
|
if (ret < 0)
|
|
{
|
|
goto err_with_lock;
|
|
}
|
|
|
|
ret = zipfs_convert_result(unzGoToNextFile(zdir->uf));
|
|
if (ret == -ENOENT)
|
|
{
|
|
if (zdir->last == false)
|
|
{
|
|
ret = OK;
|
|
zdir->last = true;
|
|
}
|
|
}
|
|
|
|
err_with_lock:
|
|
nxmutex_unlock(&zdir->lock);
|
|
return ret;
|
|
}
|
|
|
|
static int zipfs_rewinddir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct zipfs_dir_s *zdir = (FAR struct zipfs_dir_s *)dir;
|
|
int ret;
|
|
|
|
nxmutex_lock(&zdir->lock);
|
|
zdir->last = false;
|
|
ret = zipfs_convert_result(unzGoToFirstFile(zdir->uf));
|
|
nxmutex_unlock(&zdir->lock);
|
|
return ret;
|
|
}
|
|
|
|
static int zipfs_bind(FAR struct inode *driver, FAR const void *data,
|
|
FAR void **handle)
|
|
{
|
|
FAR struct zipfs_mountpt_s *fs;
|
|
unzFile uf;
|
|
|
|
if (data == NULL)
|
|
{
|
|
return -ENODEV;
|
|
}
|
|
|
|
fs = kmm_zalloc(sizeof(struct zipfs_mountpt_s) + strlen(data));
|
|
if (fs == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
uf = unzOpen2_64(data, &zipfs_real_ops);
|
|
if (uf == NULL)
|
|
{
|
|
kmm_free(fs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
unzClose(uf);
|
|
strcpy(fs->abspath, data);
|
|
*handle = fs;
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int zipfs_unbind(FAR void *handle, FAR struct inode **driver,
|
|
unsigned int flags)
|
|
{
|
|
kmm_free(handle);
|
|
return OK;
|
|
}
|
|
|
|
static int zipfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
|
|
{
|
|
buf->f_type = ZIPFS_MAGIC;
|
|
buf->f_namelen = NAME_MAX;
|
|
return OK;
|
|
}
|
|
|
|
static int zipfs_stat(FAR struct inode *mountpt,
|
|
FAR const char *relpath, FAR struct stat *buf)
|
|
{
|
|
FAR struct zipfs_mountpt_s *fs;
|
|
unzFile uf;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(mountpt && mountpt->i_private);
|
|
|
|
if (relpath[0] == 0)
|
|
{
|
|
buf->st_mode = S_IFDIR;
|
|
return OK;
|
|
}
|
|
|
|
fs = mountpt->i_private;
|
|
uf = unzOpen2_64(fs->abspath, &zipfs_real_ops);
|
|
if (uf == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = zipfs_convert_result(unzLocateFile(uf, relpath, 0));
|
|
if (ret < 0)
|
|
{
|
|
unzClose(uf);
|
|
return ret;
|
|
}
|
|
|
|
ret = zipfs_stat_common(uf, buf);
|
|
|
|
unzClose(uf);
|
|
return ret;
|
|
}
|