Skip to content

Commit

Permalink
fs: basic bcachefs support
Browse files Browse the repository at this point in the history
This adds mkfs/get_info support for Bcachefs.
  • Loading branch information
jelly committed Sep 23, 2023
1 parent 7a0f547 commit 35046e0
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Following storage technologies are supported by libblockdev
- partitions
- MSDOS, GPT
- filesystem operations
- ext2, ext3, ext4, xfs, vfat, ntfs, exfat, btrfs, f2fs, nilfs2, udf
- ext2, ext3, ext4, xfs, vfat, ntfs, exfat, btrfs, f2fs, nilfs2, udf, bcachefs
- mounting
- LVM
- thin provisioning, LVM RAID, cache, LVM VDO
Expand Down
86 changes: 86 additions & 0 deletions src/lib/plugin_apis/fs.api
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,63 @@ GType bd_fs_udf_info_get_type () {
return type;
}

/**
* BDFSBcachefsInfo:
* @uuid: uuid of the filesystem
* @size: size of the filesystem in bytes
* @free_space: free space on the filesystem in bytes
*/
typedef struct BDFSBcachefsInfo {
gchar *uuid;
guint64 size;
guint64 free_space;
} BDFSBcachefsInfo;

/**
* bd_fs_bcachefs_info_copy: (skip)
* @data: (nullable): %BDFSBcachefsInfo to copy
*
* Creates a new copy of @data.
*/
BDFSBcachefsInfo* bd_fs_bcachefs_info_copy (BDFSBcachefsInfo *data) {
if (data == NULL)
return NULL;

BDFSBcachefsInfo *ret = g_new0 (BDFSBcachefsInfo, 1);

ret->uuid = g_strdup (data->uuid);
ret->size = data->size;
ret->free_space = data->free_space;

return ret;
}

/**
* bd_fs_bcachefs_info_free: (skip)
* @data: (nullable): %BDFSBcachefsInfo to free
*
* Frees @data.
*/
void bd_fs_bcachefs_info_free (BDFSBcachefsInfo *data) {
if (data == NULL)
return;

g_free (data->uuid);
g_free (data);
}

GType bd_fs_bcachefs_info_get_type () {
static GType type = 0;

if (G_UNLIKELY(type == 0)) {
type = g_boxed_type_register_static("BDFSBcachefsInfo",
(GBoxedCopyFunc) bd_fs_bcachefs_info_copy,
(GBoxedFreeFunc) bd_fs_bcachefs_info_free);
}

return type;
}

/**
* bd_fs_is_tech_avail:
* @tech: the queried tech
Expand Down Expand Up @@ -2524,6 +2581,35 @@ gboolean bd_fs_udf_check_uuid (const gchar *uuid, GError **error);
*/
BDFSUdfInfo* bd_fs_udf_get_info (const gchar *device, GError **error);

/**
* bd_fs_bcachefs_mkfs:
* @device: the device to create a new bcachefs fs on
* @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
* passed to the 'mkfs.bcachefs' utility)
* @error: (out) (optional): place to store error (if any)
*
* Returns: whether a new bcachefs fs was successfully created on @device or not
*
* Tech category: %BD_FS_TECH_BCACHEFS-%BD_FS_TECH_MODE_MKFS
*
*/
gboolean bd_fs_bcachefs_mkfs (const gchar *device, const BDExtraArg **extra, GError **error);

/**
* bd_fs_bcachefs_get_info:
* @mpoint: a mountpoint of the bcachefs filesystem to get information about
* @error: (out) (optional): place to store error (if any)
*
* Returns: (transfer full): information about the file system on @device or
* %NULL in case of error
*
* Note: This function WON'T WORK for multi device bcachefs filesystems,
* for more complicated setups use the bcachefs plugin instead.
*
* Tech category: %BD_FS_TECH_BCACHEFS-%BD_FS_TECH_MODE_QUERY
*/
BDFSBcachefsInfo* bd_fs_bcachefs_get_info (const gchar *mpoint, GError **error);

typedef enum {
BD_FS_SUPPORT_SET_LABEL = 1 << 1,
BD_FS_SUPPORT_SET_UUID = 1 << 2
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ typedef enum {

/* XXX: where the file systems start at the enum of technologies */
#define BD_FS_OFFSET 2
#define BD_FS_LAST_FS 13
#define BD_FS_LAST_FS 14
typedef enum {
BD_FS_TECH_GENERIC = 0,
BD_FS_TECH_MOUNT = 1,
Expand All @@ -38,6 +38,7 @@ typedef enum {
BD_FS_TECH_EXFAT = 10,
BD_FS_TECH_BTRFS = 11,
BD_FS_TECH_UDF = 12,
BD_FS_TECH_BCACHEFS = 13,
} BDFSTech;

/* XXX: number of the highest bit of all modes */
Expand Down Expand Up @@ -80,3 +81,4 @@ gboolean bd_fs_is_tech_avail (BDFSTech tech, guint64 mode, GError **error);
#include "fs/exfat.h"
#include "fs/btrfs.h"
#include "fs/udf.h"
#include "fs/bcachefs.h"
3 changes: 2 additions & 1 deletion src/plugins/fs/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ libbd_fs_la_SOURCES = ../check_deps.c ../check_deps.h \
nilfs.c nilfs.h \
exfat.c exfat.h \
btrfs.c btrfs.h \
udf.c udf.h
udf.c udf.h \
bcachefs.c bcachefs.h

libincludefsdir = $(includedir)/blockdev/fs/
libincludefs_HEADERS = ext.h \
Expand Down
229 changes: 229 additions & 0 deletions src/plugins/fs/bcachefs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* Copyright (C) 2023 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Jelle van der Waa <[email protected]>
*/

#include <blockdev/utils.h>
#include <check_deps.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <stdio.h>

#include "bcachefs.h"
#include "fs.h"
#include "common.h"

static volatile guint avail_deps = 0;
static GMutex deps_check_lock;

#define DEPS_MKFSBCACHEFS 0
#define DEPS_MKFSBCACHEFS_MASK (1 << DEPS_MKFSBCACHEFS)
#define DEPS_BCACHEFSCK 1
#define DEPS_BCACHEFSCK_MASK (1 << DEPS_BCACHEFSCK)
#define DEPS_BCACHEFS 2
#define DEPS_BCACHEFS_MASK (1 << DEPS_BCACHEFS)

#define DEPS_LAST 3

static const UtilDep deps[DEPS_LAST] = {
{"mkfs.bcachefs", NULL, NULL, NULL},
{"fsck.bcachefs", NULL, NULL, NULL},
{"bcachefs", NULL, NULL, NULL},
};

static guint32 fs_mode_util[BD_FS_MODE_LAST+1] = {
DEPS_MKFSBCACHEFS_MASK, /* mkfs */
0, /* wipe */
DEPS_BCACHEFSCK_MASK, /* check */
DEPS_BCACHEFSCK_MASK, /* repair */
DEPS_BCACHEFS_MASK, /* set-label */
DEPS_BCACHEFS_MASK, /* query */
DEPS_BCACHEFS_MASK, /* resize */
};

#define UNUSED __attribute__((unused))

/**
* bd_fs_bcachefs_is_tech_avail:
* @tech: the queried tech
* @mode: a bit mask of queried modes of operation (#BDFSTechMode) for @tech
* @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
*
* Returns: whether the @tech-@mode combination is available -- supported by the
* plugin implementation and having all the runtime dependencies available
*/
gboolean __attribute__ ((visibility ("hidden")))
bd_fs_bcachefs_is_tech_avail (BDFSTech tech UNUSED, guint64 mode, GError **error) {
guint32 required = 0;
guint i = 0;

for (i = 0; i <= BD_FS_MODE_LAST; i++)
if (mode & (1 << i))
required |= fs_mode_util[i];

return check_deps (&avail_deps, required, deps, DEPS_LAST, &deps_check_lock, error);
}

/**
* bd_fs_bcachefs_info_copy: (skip)
*
* Creates a new copy of @data.
*/
BDFSBcachefsInfo* bd_fs_bcachefs_info_copy (BDFSBcachefsInfo *data) {
if (data == NULL)
return NULL;

g_info("copying");
BDFSBcachefsInfo *ret = g_new0 (BDFSBcachefsInfo, 1);

ret->uuid = g_strdup (data->uuid);
ret->size = data->size;
ret->free_space = data->free_space;

return ret;
}

/**
* bd_fs_bcachefs_info_free: (skip)
*
* Frees @data.
*/
void bd_fs_bcachefs_info_free (BDFSBcachefsInfo *data) {
if (data == NULL)
return;
g_free (data->uuid);
g_free (data);
}

BDExtraArg __attribute__ ((visibility ("hidden")))
**bd_fs_bcachefs_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
GPtrArray *options_array = g_ptr_array_new ();
const BDExtraArg **extra_p = NULL;

if (options->label && g_strcmp0 (options->label, "") != 0)
g_ptr_array_add (options_array, bd_extra_arg_new ("-L", options->label));

if (options->uuid && g_strcmp0 (options->uuid, "") != 0)
g_ptr_array_add (options_array, bd_extra_arg_new ("-U", options->uuid));

if (options->force)
g_ptr_array_add (options_array, bd_extra_arg_new ("-f", ""));

if (extra) {
for (extra_p = extra; *extra_p; extra_p++)
g_ptr_array_add (options_array, bd_extra_arg_copy ((BDExtraArg *) *extra_p));
}

g_ptr_array_add (options_array, NULL);

return (BDExtraArg **) g_ptr_array_free (options_array, FALSE);
}

/**
* bd_fs_bcachefs_mkfs:
* @device: the device to create a new bcachefs fs on
* @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
* passed to the 'mkfs.bcachefs' utility)
* @error: (out) (optional): place to store error (if any)
*
* Returns: whether a new bcachefs fs was successfully created on @device or not
*
* Tech category: %BD_FS_TECH_BCACHEFS-%BD_FS_TECH_MODE_MKFS
*
*/
gboolean bd_fs_bcachefs_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
const gchar *args[3] = {"mkfs.bcachefs", device, NULL};

if (!check_deps (&avail_deps, DEPS_MKFSBCACHEFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
return FALSE;

return bd_utils_exec_and_report_error (args, extra, error);
}

/**
* bd_fs_bcachefs_get_info:
* @mpoint: a mountpoint of the bcachefs filesystem to get information about
* @error: (out) (optional): place to store error (if any)
*
* Returns: (transfer full): information about the file system on @device or
* %NULL in case of error
*
* Note: This function WON'T WORK for multi device bcachefs filesystems,
* for more complicated setups use the btrfs plugin instead.
*
* Tech category: %BD_FS_TECH_BCACHEFS-%BD_FS_TECH_MODE_QUERY
*/
BDFSBcachefsInfo* bd_fs_bcachefs_get_info (const gchar *mpoint, GError **error) {
const gchar *args[5] = {"bcachefs", "fs", "usage", mpoint, NULL};
gboolean success = FALSE;
gchar *output = NULL;
gchar const * const pattern = "Filesystem:\\s+(?P<uuid>\\S+)\\s+" \
"Size:\\s+(?P<size>\\d+)\\s+" \
"Used:\\s+(?P<used>\\d+)\\s+\\S+" ;
GRegex *regex = NULL;
GMatchInfo *match_info = NULL;
BDFSBcachefsInfo *ret = NULL;
gchar *item = NULL;
guint64 used = 0;

if (!check_deps (&avail_deps, DEPS_BCACHEFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
return NULL;

regex = g_regex_new (pattern, G_REGEX_EXTENDED, 0, error);
if (!regex) {
bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to create new GRegex");
/* error is already populated */
return NULL;
}

success = bd_utils_exec_and_capture_output (args, NULL, &output, error);
if (!success) {
/* error is already populated */
return NULL;
}

success = g_regex_match (regex, output, 0, &match_info);
if (!success) {
g_regex_unref (regex);
g_match_info_free (match_info);
g_free (output);
return NULL;
}

ret = g_new (BDFSBcachefsInfo, 1);

ret->uuid = g_match_info_fetch_named (match_info, "uuid");
item = g_match_info_fetch_named (match_info, "size");
ret->size = g_ascii_strtoull (item, NULL, 0);
g_free (item);
item = g_match_info_fetch_named (match_info, "used");
used = g_ascii_strtoull (item, NULL, 0);
g_free (item);

g_match_info_free (match_info);
g_regex_unref (regex);
g_free (output);

// TODO error out if there are more then 1 devices
// TODO: detect label
ret->free_space = ret->size - used;

return ret;
}
19 changes: 19 additions & 0 deletions src/plugins/fs/bcachefs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <glib.h>
#include <blockdev/utils.h>

#ifndef BD_FS_BCACHEFS
#define BD_FS_BCACHEFS

typedef struct BDFSBcachefsInfo {
gchar *uuid;
guint64 size;
guint64 free_space;
} BDFSBcachefsInfo;

BDFSBcachefsInfo* bd_fs_bcachefs_info_copy (BDFSBcachefsInfo *data);
void bd_fs_bcachefs_info_free (BDFSBcachefsInfo *data);

gboolean bd_fs_bcachefs_mkfs (const gchar *device, const BDExtraArg **extra, GError **error);
BDFSBcachefsInfo* bd_fs_bcachefs_get_info (const gchar *device, GError **error);

#endif /* BD_FS_BCACHEFS */
Loading

0 comments on commit 35046e0

Please sign in to comment.