nuttx/fs/zipfs/zip_vfs.c
anjiahao a2c5cb729a fs:support zipfs,can mount zipfile
we can mount a zipfile in nuttx use mount command like this:

mount -t zipfs -o /data/test.zip /zip

The zipfs is a read only file system,The advantage is that it
does not occupy additional space when reading the decompressed file.

When used, reading and decompression operations are simultaneous.
The known disadvantage is that when using seek to read forward,
it will reopen and cause slow speed problems.

Signed-off-by: anjiahao <anjiahao@xiaomi.com>
2023-09-27 01:06:04 +08:00

656 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, /* 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;
}