Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 2 additions & 0 deletions fs/driver/Make.defs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y)
CSRCS += fs_registerblockdriver.c fs_unregisterblockdriver.c
CSRCS += fs_findblockdriver.c fs_openblockdriver.c fs_closeblockdriver.c
CSRCS += fs_blockpartition.c fs_findmtddriver.c fs_closemtddriver.c
CSRCS += fs_blockmerge.c


ifeq ($(CONFIG_MTD),y)
CSRCS += fs_registermtddriver.c fs_unregistermtddriver.c
Expand Down
331 changes: 331 additions & 0 deletions fs/driver/fs_blockmerge.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
/****************************************************************************
* 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;
}
28 changes: 28 additions & 0 deletions include/nuttx/fs/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,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