nuttx-mirror/fs/driver/fs_blockmerge.c
wanggang26 929bbf0be8 fs: add merge partitions support
Usage example for merging the factory partition and reserve
partition into the merge partition:
register_merge_blockdriver("/dev/merge", "/dev/factory", "/dev/reserve",
NULL)

Signed-off-by: wanggang26 <wanggang26@xiaomi.com>
2024-11-07 14:59:23 +08:00

331 lines
8.8 KiB
C

/****************************************************************************
* fs/driver/fs_blockmerge.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 <nuttx/config.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <stdarg.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/partition.h>
/****************************************************************************
* Private Types
****************************************************************************/
struct merge_part_s
{
FAR struct inode *inode;
struct geometry geo;
char path[PATH_MAX];
};
struct merge_priv_s
{
size_t npart;
struct merge_part_s part[0];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int merge_open(FAR struct inode *inode);
static int merge_close(FAR struct inode *inode);
static ssize_t merge_read(FAR struct inode *inode, FAR unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors);
static ssize_t merge_write(FAR struct inode *inode,
FAR const unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors);
static int merge_geometry(FAR struct inode *inode,
FAR struct geometry *geometry);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct block_operations g_merge_bops =
{
merge_open, /* open */
merge_close, /* close */
merge_read, /* read */
merge_write, /* write */
merge_geometry, /* geometry */
NULL /* ioctl */
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int merge_open(FAR struct inode *inode)
{
FAR struct merge_priv_s *priv;
int ret = 0;
size_t i;
DEBUGASSERT(inode && inode->i_private);
priv = inode->i_private;
for (i = 0; i < priv->npart; i++)
{
ret = open_blockdriver(priv->part[i].path, 0, &inode);
if (ret < 0)
{
ferr("ERROR: Failed to open block driver for %s\n",
priv->part[i].path);
goto err_with_open;
}
priv->part[i].inode = inode;
if (inode->u.i_bops->geometry == NULL ||
inode->u.i_bops->geometry(inode, &priv->part[i].geo) < 0 ||
priv->part[i].geo.geo_sectorsize <= 0 ||
priv->part[i].geo.geo_nsectors <= 0)
{
ferr("ERROR: Failed to get geometry for %s\n", priv->part[i].path);
ret = -EINVAL;
goto err_with_inode;
}
if (priv->part[0].geo.geo_sectorsize !=
priv->part[i].geo.geo_sectorsize)
{
ferr("ERROR: Inconsistent sectorsize!\n");
ret = -EINVAL;
goto err_with_inode;
}
finfo("[%s] nsectors: %" PRIuOFF " sectorsize:%u\n",
priv->part[i].path, priv->part[i].geo.geo_nsectors,
priv->part[i].geo.geo_sectorsize);
}
return ret;
err_with_inode:
close_blockdriver(priv->part[i].inode);
err_with_open:
while (i--)
{
close_blockdriver(priv->part[i].inode);
}
return ret;
}
static int merge_close(FAR struct inode *inode)
{
FAR struct merge_priv_s *priv;
size_t i;
DEBUGASSERT(inode && inode->i_private);
priv = inode->i_private;
for (i = 0; i < priv->npart; i++)
{
int ret = close_blockdriver(priv->part[i].inode);
if (ret < 0)
{
ferr("ERROR: Failed to close block driver for %s\n",
priv->part[i].path);
return ret;
}
}
return OK;
}
static ssize_t merge_read(FAR struct inode *inode, FAR unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors)
{
FAR struct merge_priv_s *priv;
ssize_t nread = 0;
size_t ntotal = 0;
size_t i;
DEBUGASSERT(inode && inode->i_private);
priv = inode->i_private;
for (i = 0; nsectors > 0 && i < priv->npart; i++)
{
if ((startsector >= priv->part[i].geo.geo_nsectors))
{
startsector -= priv->part[i].geo.geo_nsectors;
continue;
}
inode = priv->part[i].inode;
nread = MIN(priv->part[i].geo.geo_nsectors - startsector, nsectors);
nread = inode->u.i_bops->read(inode, buffer, startsector, nread);
if (nread < 0)
{
break;
}
buffer += nread * priv->part[i].geo.geo_sectorsize;
nsectors -= nread;
ntotal += nread;
startsector = 0;
}
/* On success, return the number of blocks read */
return ntotal ? ntotal : nread;
}
static ssize_t merge_write(FAR struct inode *inode,
FAR const unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors)
{
FAR struct merge_priv_s *priv;
ssize_t nwritten = 0;
size_t ntotal = 0;
size_t i;
DEBUGASSERT(inode && inode->i_private);
priv = inode->i_private;
for (i = 0; nsectors > 0 && i < priv->npart; i++)
{
if (startsector >= priv->part[i].geo.geo_nsectors)
{
startsector -= priv->part[i].geo.geo_nsectors;
continue;
}
inode = priv->part[i].inode;
nwritten = MIN(priv->part[i].geo.geo_nsectors - startsector, nsectors);
nwritten = inode->u.i_bops->write(inode, buffer, startsector,
nwritten);
if (nwritten < 0)
{
break;
}
buffer += nwritten * priv->part[i].geo.geo_sectorsize;
nsectors -= nwritten;
ntotal += nwritten;
startsector = 0;
}
/* On success, return the number of blocks written */
return ntotal ? ntotal : nwritten;
}
static int merge_geometry(FAR struct inode *inode,
FAR struct geometry *geometry)
{
DEBUGASSERT(inode && inode->i_private);
if (geometry)
{
FAR struct merge_priv_s *priv = inode->i_private;
size_t i;
memcpy(geometry, &priv->part[0].geo, sizeof(*geometry));
for (i = 1; i < priv->npart; i++)
{
geometry->geo_nsectors += priv->part[i].geo.geo_nsectors;
}
finfo("nsectors: %" PRIuOFF " sectorsize:%u\n",
geometry->geo_nsectors, geometry->geo_sectorsize);
return OK;
}
return -EINVAL;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int register_merge_blockdriver(FAR const char *merge, ...)
{
FAR struct merge_priv_s *priv;
FAR const char *name;
va_list args;
size_t i;
int ret;
va_start(args, merge);
for (i = 0; ; i++)
{
name = va_arg(args, FAR const char *);
if (name == NULL)
{
break;
}
}
va_end(args);
if (i < 2)
{
ferr("ERROR: invalid number of partitions\n");
return -EINVAL;
}
priv = kmm_zalloc(sizeof(struct merge_priv_s) + i *
sizeof(struct merge_part_s));
if (priv == NULL)
{
ferr("ERROR: out of memory\n");
return -ENOMEM;
}
priv->npart = i;
va_start(args, merge);
for (i = 0; i < priv->npart; i++)
{
name = va_arg(args, FAR const char *);
strlcpy(priv->part[i].path, name, PATH_MAX);
}
ret = register_blockdriver(merge, &g_merge_bops, 0, priv);
if (ret < 0)
{
ferr("ERROR: register blockdriver failed: %d\n", ret);
goto err;
}
va_end(args);
return ret;
err:
va_end(args);
kmm_free(priv);
return ret;
}