Skip to content

Commit

Permalink
fs: add merge partitions support
Browse files Browse the repository at this point in the history
Usage example for merging the aaa partition and bbb partition into the merge partition:
register_merge_blockdriver("/dev/merge", "/dev/aaa", "/dev/bbb",
NULL)

Signed-off-by: wanggang26 <[email protected]>
  • Loading branch information
gneworld authored and xiaoxiang781216 committed Jan 12, 2025
1 parent 6ffd007 commit e4aad73
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 0 deletions.
1 change: 1 addition & 0 deletions fs/driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ if(NOT CONFIG_DISABLE_MOUNTPOINT)
fs_closeblockdriver.c
fs_blockpartition.c
fs_findmtddriver.c
fs_blockmerge.c
fs_closemtddriver.c)

if(CONFIG_MTD)
Expand Down
1 change: 1 addition & 0 deletions fs/driver/Make.defs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

CSRCS += fs_registerdriver.c fs_unregisterdriver.c
CSRCS += fs_registerpipedriver.c fs_unregisterpipedriver.c
CSRCS += fs_blockmerge.c

# Don't built-in block driver support if there are no mountpoints

Expand Down
323 changes: 323 additions & 0 deletions fs/driver/fs_blockmerge.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
/****************************************************************************
* 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 <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <sys/param.h>
#include <inttypes.h>
#include <debug.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 = (FAR struct merge_priv_s *)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 = (FAR struct merge_priv_s *)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, 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 = (FAR struct merge_priv_s *)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,
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 = (FAR struct merge_priv_s *)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, 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;
}
28 changes: 28 additions & 0 deletions include/nuttx/fs/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,34 @@ int register_blockdriver(FAR const char *path,
mode_t mode, FAR void *priv);
#endif

/****************************************************************************
* Name: register_merge_blockdriver
*
* Description:
* This function registers a block driver that presents a contiguous block
* device composed of multiple, non-contiguous partitions. The partitions
* are specified by the variable argument list which has 'nparts' elements.
*
* Input Parameters:
* merge - The partition name to be merged.
* ... - The variable argument list for partition names.
*
* Usage example for merging the factory partition and reserve partition
* into the merge partition:
*
* register_merge_blockdriver("/dev/merge", "/dev/factory",
* "/dev/reserve", NULL);
*
* Returned Value:
* Zero on success;
* Negated errno value is returned on a failure
*
****************************************************************************/

#ifndef CONFIG_DISABLE_MOUNTPOINT
int register_merge_blockdriver(FAR const char *merge, ...);
#endif

/****************************************************************************
* Name: register_blockpartition
*
Expand Down

0 comments on commit e4aad73

Please sign in to comment.