|
Packit |
2ba279 |
/*
|
|
Packit |
2ba279 |
* Copyright (C) 2017 Red Hat, Inc.
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* This library is free software; you can redistribute it and/or
|
|
Packit |
2ba279 |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
2ba279 |
* License as published by the Free Software Foundation; either
|
|
Packit |
2ba279 |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
2ba279 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
2ba279 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
2ba279 |
* Lesser General Public License for more details.
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
2ba279 |
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Author: Vratislav Podzimek <vpodzime@redhat.com>
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
#include <blockdev/utils.h>
|
|
Packit |
2ba279 |
#include <check_deps.h>
|
|
Packit |
2ba279 |
#include <string.h>
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
#include "ntfs.h"
|
|
Packit |
2ba279 |
#include "fs.h"
|
|
Packit |
2ba279 |
#include "common.h"
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
static volatile guint avail_deps = 0;
|
|
Packit |
2ba279 |
static GMutex deps_check_lock;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
#define DEPS_MKNTFS 0
|
|
Packit |
2ba279 |
#define DEPS_MKNTFS_MASK (1 << DEPS_MKNTFS)
|
|
Packit |
2ba279 |
#define DEPS_NTFSFIX 1
|
|
Packit |
2ba279 |
#define DEPS_NTFSFIX_MASK (1 << DEPS_NTFSFIX)
|
|
Packit |
2ba279 |
#define DEPS_NTFSRESIZE 2
|
|
Packit |
2ba279 |
#define DEPS_NTFSRESIZE_MASK (1 << DEPS_NTFSRESIZE)
|
|
Packit |
2ba279 |
#define DEPS_NTFSLABEL 3
|
|
Packit |
2ba279 |
#define DEPS_NTFSLABEL_MASK (1 << DEPS_NTFSLABEL)
|
|
Packit |
2ba279 |
#define DEPS_NTFSCLUSTER 4
|
|
Packit |
2ba279 |
#define DEPS_NTFSCLUSTER_MASK (1 << DEPS_NTFSCLUSTER)
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
#define DEPS_LAST 5
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
static const UtilDep deps[DEPS_LAST] = {
|
|
Packit |
2ba279 |
{"mkntfs", NULL, NULL, NULL},
|
|
Packit |
2ba279 |
{"ntfsfix", NULL, NULL, NULL},
|
|
Packit |
2ba279 |
{"ntfsresize", NULL, NULL, NULL},
|
|
Packit |
2ba279 |
{"ntfslabel", NULL, NULL, NULL},
|
|
Packit |
2ba279 |
{"ntfscluster", NULL, NULL, NULL},
|
|
Packit |
2ba279 |
};
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
static guint32 fs_mode_util[BD_FS_MODE_LAST+1] = {
|
|
Packit |
2ba279 |
/* mkfs wipe check repair set-label query resize */
|
|
Packit |
2ba279 |
DEPS_MKNTFS_MASK, 0, DEPS_NTFSFIX_MASK, DEPS_NTFSFIX_MASK, DEPS_NTFSLABEL_MASK, DEPS_NTFSCLUSTER_MASK, DEPS_NTFSRESIZE_MASK
|
|
Packit |
2ba279 |
};
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
#define UNUSED __attribute__((unused))
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_is_tech_avail:
|
|
Packit |
2ba279 |
* @tech: the queried tech
|
|
Packit |
2ba279 |
* @mode: a bit mask of queried modes of operation (#BDFSTechMode) for @tech
|
|
Packit |
2ba279 |
* @error: (out): place to store error (details about why the @tech-@mode combination is not available)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Returns: whether the @tech-@mode combination is available -- supported by the
|
|
Packit |
2ba279 |
* plugin implementation and having all the runtime dependencies available
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
gboolean __attribute__ ((visibility ("hidden")))
|
|
Packit |
2ba279 |
bd_fs_ntfs_is_tech_avail (BDFSTech tech UNUSED, guint64 mode, GError **error) {
|
|
Packit |
2ba279 |
guint32 required = 0;
|
|
Packit |
2ba279 |
guint i = 0;
|
|
Packit |
2ba279 |
for (i = 0; i <= BD_FS_MODE_LAST; i++)
|
|
Packit |
2ba279 |
if (mode & (1 << i))
|
|
Packit |
2ba279 |
required |= fs_mode_util[i];
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
return check_deps (&avail_deps, required, deps, DEPS_LAST, &deps_check_lock, error);
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_info_copy: (skip)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Creates a new copy of @data.
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
BDFSNtfsInfo* bd_fs_ntfs_info_copy (BDFSNtfsInfo *data) {
|
|
Packit |
2ba279 |
if (data == NULL)
|
|
Packit |
2ba279 |
return NULL;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
BDFSNtfsInfo *ret = g_new0 (BDFSNtfsInfo, 1);
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
ret->size = data->size;
|
|
Packit |
2ba279 |
ret->free_space = data->free_space;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
return ret;
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_info_free: (skip)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Frees @data.
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
void bd_fs_ntfs_info_free (BDFSNtfsInfo *data) {
|
|
Packit |
2ba279 |
g_free (data);
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_mkfs:
|
|
Packit |
2ba279 |
* @device: the device to create a new ntfs fs on
|
|
Packit |
2ba279 |
* @extra: (allow-none) (array zero-terminated=1): extra options for the creation (right now
|
|
Packit |
2ba279 |
* passed to the 'mkntfs' utility)
|
|
Packit |
2ba279 |
* @error: (out): place to store error (if any)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Returns: whether a new NTFS fs was successfully created on @device or not
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Tech category: %BD_FS_TECH_NTFS-%BD_FS_TECH_MODE_MKFS
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
gboolean bd_fs_ntfs_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
|
|
Packit |
2ba279 |
const gchar *args[5] = {"mkntfs", "-f", "-F", device, NULL};
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
if (!check_deps (&avail_deps, DEPS_MKNTFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
return bd_utils_exec_and_report_error (args, extra, error);
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_wipe:
|
|
Packit |
2ba279 |
* @device: the device to wipe an ntfs signature from
|
|
Packit |
2ba279 |
* @error: (out): place to store error (if any)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Returns: whether an ntfs signature was successfully wiped from the @device or not
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Tech category: %BD_FS_TECH_NTFS-%BD_FS_TECH_MODE_WIPE
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
gboolean bd_fs_ntfs_wipe (const gchar *device, GError **error) {
|
|
Packit |
2ba279 |
return wipe_fs (device, "ntfs", TRUE, error);
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_check:
|
|
Packit |
2ba279 |
* @device: the device containing the file system to check
|
|
Packit |
2ba279 |
* @error: (out): place to store error (if any)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Returns: whether an ntfs file system on the @device is clean or not
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Tech category: %BD_FS_TECH_NTFS-%BD_FS_TECH_MODE_CHECK
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
gboolean bd_fs_ntfs_check (const gchar *device, GError **error) {
|
|
Packit |
2ba279 |
const gchar *args[4] = {"ntfsfix", "-n", device, NULL};
|
|
Packit |
2ba279 |
gint status = 0;
|
|
Packit |
2ba279 |
gboolean ret = FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
if (!check_deps (&avail_deps, DEPS_NTFSFIX_MASK, deps, DEPS_LAST, &deps_check_lock, error))
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
ret = bd_utils_exec_and_report_status_error (args, NULL, &status, error);
|
|
Packit |
2ba279 |
if (!ret && (status == 1)) {
|
|
Packit |
2ba279 |
/* no error should be reported for exit code 1 -- Recoverable errors have been detected */
|
|
Packit |
2ba279 |
g_clear_error (error);
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
return ret;
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_repair:
|
|
Packit |
2ba279 |
* @device: the device containing the file system to repair
|
|
Packit |
2ba279 |
* @error: (out): place to store error (if any)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Returns: whether an NTFS file system on the @device was successfully repaired
|
|
Packit |
2ba279 |
* (if needed) or not (error is set in that case)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Tech category: %BD_FS_TECH_NTFS-%BD_FS_TECH_MODE_REPAIR
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
gboolean bd_fs_ntfs_repair (const gchar *device, GError **error) {
|
|
Packit |
2ba279 |
const gchar *args[4] = {"ntfsfix", "-d", device, NULL};
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
if (!check_deps (&avail_deps, DEPS_NTFSFIX_MASK, deps, DEPS_LAST, &deps_check_lock, error))
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
return bd_utils_exec_and_report_error (args, NULL, error);
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_set_label:
|
|
Packit |
2ba279 |
* @device: the device containing the file system to set the label for
|
|
Packit |
2ba279 |
* @label: label to set
|
|
Packit |
2ba279 |
* @error: (out): place to store error (if any)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Returns: whether the label of the NTFS file system on the @device was
|
|
Packit |
2ba279 |
* successfully set or not
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Tech category: %BD_FS_TECH_NTFS-%BD_FS_TECH_MODE_SET_LABEL
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
gboolean bd_fs_ntfs_set_label (const gchar *device, const gchar *label, GError **error) {
|
|
Packit |
2ba279 |
const gchar *args[4] = {"ntfslabel", device, label, NULL};
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
if (!check_deps (&avail_deps, DEPS_NTFSLABEL_MASK, deps, DEPS_LAST, &deps_check_lock, error))
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
return bd_utils_exec_and_report_error (args, NULL, error);
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_resize:
|
|
Packit |
2ba279 |
* @device: the device the file system of which to resize
|
|
Packit |
2ba279 |
* @new_size: new requested size for the file system in bytes (if 0, the file system
|
|
Packit |
2ba279 |
* is adapted to the underlying block device)
|
|
Packit |
2ba279 |
* @error: (out): place to store error (if any)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Returns: whether the file system on @device was successfully resized or not
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Tech category: %BD_FS_TECH_NTFS-%BD_FS_TECH_MODE_RESIZE
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
gboolean bd_fs_ntfs_resize (const gchar *device, guint64 new_size, GError **error) {
|
|
Packit |
2ba279 |
const gchar *args[5] = {"ntfsresize", NULL, NULL, NULL, NULL};
|
|
Packit |
2ba279 |
gboolean ret = FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
if (!check_deps (&avail_deps, DEPS_NTFSRESIZE_MASK, deps, DEPS_LAST, &deps_check_lock, error))
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
if (new_size != 0) {
|
|
Packit |
2ba279 |
args[1] = "-s";
|
|
Packit |
2ba279 |
args[2] = g_strdup_printf ("%"G_GUINT64_FORMAT, new_size);
|
|
Packit |
2ba279 |
args[3] = device;
|
|
Packit |
2ba279 |
} else {
|
|
Packit |
2ba279 |
args[1] = device;
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
ret = bd_utils_exec_and_report_error (args, NULL, error);
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
g_free ((gchar *) args[2]);
|
|
Packit |
2ba279 |
return ret;
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/**
|
|
Packit |
2ba279 |
* bd_fs_ntfs_get_info:
|
|
Packit |
2ba279 |
* @device: the device containing the file system to get info for (device must
|
|
Packit |
2ba279 |
not be mounted, trying to get info for a mounted device will result
|
|
Packit |
2ba279 |
in an error)
|
|
Packit |
2ba279 |
* @error: (out): place to store error (if any)
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Returns: (transfer full): information about the file system on @device or
|
|
Packit |
2ba279 |
* %NULL in case of error
|
|
Packit |
2ba279 |
*
|
|
Packit |
2ba279 |
* Tech category: %BD_FS_TECH_NTFS-%BD_FS_TECH_MODE_QUERY
|
|
Packit |
2ba279 |
*/
|
|
Packit |
2ba279 |
BDFSNtfsInfo* bd_fs_ntfs_get_info (const gchar *device, GError **error) {
|
|
Packit |
2ba279 |
const gchar *args[3] = {"ntfscluster", device, NULL};
|
|
Packit |
2ba279 |
gboolean success = FALSE;
|
|
Packit |
2ba279 |
gchar *output = NULL;
|
|
Packit |
2ba279 |
BDFSNtfsInfo *ret = NULL;
|
|
Packit |
2ba279 |
gchar **lines = NULL;
|
|
Packit |
2ba279 |
gchar **line_p = NULL;
|
|
Packit |
2ba279 |
gchar *val_start = NULL;
|
|
Packit |
2ba279 |
g_autofree gchar* mountpoint = NULL;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
if (!check_deps (&avail_deps, DEPS_NTFSCLUSTER_MASK, deps, DEPS_LAST, &deps_check_lock, error))
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
mountpoint = bd_fs_get_mountpoint (device, error);
|
|
Packit |
2ba279 |
if (mountpoint != NULL) {
|
|
Packit |
2ba279 |
g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_NOT_MOUNTED,
|
|
Packit |
2ba279 |
"Can't get NTFS file system information for '%s': Device is mounted.", device);
|
|
Packit |
2ba279 |
return NULL;
|
|
Packit |
2ba279 |
} else {
|
|
Packit |
2ba279 |
if (*error != NULL) {
|
|
Packit |
2ba279 |
g_prefix_error (error, "Error when trying to get mountpoint for '%s': ", device);
|
|
Packit |
2ba279 |
return NULL;
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
success = bd_utils_exec_and_capture_output (args, NULL, &output, error);
|
|
Packit |
2ba279 |
if (!success)
|
|
Packit |
2ba279 |
/* error is already populated */
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
ret = g_new0 (BDFSNtfsInfo, 1);
|
|
Packit |
2ba279 |
lines = g_strsplit (output, "\n", 0);
|
|
Packit |
2ba279 |
g_free (output);
|
|
Packit |
2ba279 |
line_p = lines;
|
|
Packit |
2ba279 |
/* find the beginning of the (data) section we are interested in */
|
|
Packit |
2ba279 |
while (line_p && *line_p && !g_str_has_prefix (*line_p, "bytes per volume"))
|
|
Packit |
2ba279 |
line_p++;
|
|
Packit |
2ba279 |
if (!line_p || !(*line_p)) {
|
|
Packit |
2ba279 |
g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse NTFS file system information");
|
|
Packit |
2ba279 |
g_strfreev (lines);
|
|
Packit |
2ba279 |
bd_fs_ntfs_info_free (ret);
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/* extract data from something like this: "bytes per volume : 998240256" */
|
|
Packit |
2ba279 |
val_start = strchr (*line_p, ':');
|
|
Packit |
2ba279 |
val_start++;
|
|
Packit |
2ba279 |
ret->size = g_ascii_strtoull (val_start, NULL, 0);
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
while (line_p && *line_p && !g_str_has_prefix (*line_p, "bytes of free space"))
|
|
Packit |
2ba279 |
line_p++;
|
|
Packit |
2ba279 |
if (!line_p || !(*line_p)) {
|
|
Packit |
2ba279 |
g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse NTFS file system information");
|
|
Packit |
2ba279 |
g_strfreev (lines);
|
|
Packit |
2ba279 |
bd_fs_ntfs_info_free (ret);
|
|
Packit |
2ba279 |
return FALSE;
|
|
Packit |
2ba279 |
}
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
/* extract data from something like this: "bytes of free space : 992759808" */
|
|
Packit |
2ba279 |
val_start = strchr (*line_p, ':');
|
|
Packit |
2ba279 |
val_start++;
|
|
Packit |
2ba279 |
ret->free_space = g_ascii_strtoull (val_start, NULL, 0);
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
g_strfreev (lines);
|
|
Packit |
2ba279 |
|
|
Packit |
2ba279 |
return ret;
|
|
Packit |
2ba279 |
}
|