pmt: e2fsprogs: cleanup

This commit is contained in:
2024-10-02 22:59:34 +03:00
parent 5cbd362d72
commit e7c51ced1b
332 changed files with 379 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
/*
* devname.c --- Support function to translate a user provided string
* identifying a device to an actual device path
*
* Copyright (C) 2022 Red Hat, Inc., Lukas Czerner <lczerner@redhat.com>
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*/
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "config.h"
#include "devname.h"
#include "nls-enable.h"
/*
* blkid_get_devname() is primarily intended for parsing "NAME=value"
* tokens. It will return the device matching the specified token, NULL if
* nothing is found, or copy of the string if it's not in "NAME=value"
* format.
* get_devname() takes the same parameters and works the same way as
* blkid_get_devname() except it can handle '=' in the file name.
*/
char *get_devname(blkid_cache cache, const char *token, const char *value)
{
int is_file = 0;
char *ret = NULL;
if (!token)
goto out;
if (value) {
ret = blkid_get_devname(cache, token, value);
goto out;
}
if (access(token, F_OK) == 0)
is_file = 1;
ret = blkid_get_devname(cache, token, NULL);
if (ret) {
/*
* In case of collision prefer the result from
* blkid_get_devname() to avoid a file masking file system with
* existing tag.
*/
if (is_file && (strcmp(ret, token) != 0)) {
fprintf(stderr,
_("Collision found: '%s' refers to both '%s' "
"and a file '%s'. Using '%s'!\n"),
token, ret, token, ret);
}
goto out;
}
if (is_file)
ret = strdup(token);
out:
return ret;
}

1542
e2fsprogs/lib/support/dict.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,706 @@
/*
* mkquota.c --- create quota files for a filesystem
*
* Aditya Kali <adityakali@google.com>
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include "ext2fs/ext2_fs.h"
#include "ext2fs/ext2fs.h"
#include "e2p/e2p.h"
#include "quotaio.h"
#include "quotaio_v2.h"
#include "quotaio_tree.h"
#include "common.h"
#include "dict.h"
/* Needed for architectures where sizeof(int) != sizeof(void *) */
#define UINT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
#define VOIDPTR_TO_UINT(ptr) ((unsigned int)(intptr_t)(ptr))
#if DEBUG_QUOTA
static void print_inode(struct ext2_inode *inode)
{
if (!inode)
return;
fprintf(stderr, " i_mode = %d\n", inode->i_mode);
fprintf(stderr, " i_uid = %d\n", inode->i_uid);
fprintf(stderr, " i_size = %d\n", inode->i_size);
fprintf(stderr, " i_atime = %d\n", inode->i_atime);
fprintf(stderr, " i_ctime = %d\n", inode->i_ctime);
fprintf(stderr, " i_mtime = %d\n", inode->i_mtime);
fprintf(stderr, " i_dtime = %d\n", inode->i_dtime);
fprintf(stderr, " i_gid = %d\n", inode->i_gid);
fprintf(stderr, " i_links_count = %d\n", inode->i_links_count);
fprintf(stderr, " i_blocks = %d\n", inode->i_blocks);
fprintf(stderr, " i_flags = %d\n", inode->i_flags);
return;
}
static void print_dquot(const char *desc, struct dquot *dq)
{
if (desc)
fprintf(stderr, "%s: ", desc);
fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n",
dq->dq_id, (long long) dq->dq_dqb.dqb_curspace,
(long long) dq->dq_dqb.dqb_bsoftlimit,
(long long) dq->dq_dqb.dqb_bhardlimit,
(long long) dq->dq_dqb.dqb_curinodes,
(long long) dq->dq_dqb.dqb_isoftlimit,
(long long) dq->dq_dqb.dqb_ihardlimit);
}
#else
static void print_dquot(const char *desc EXT2FS_ATTR((unused)),
struct dquot *dq EXT2FS_ATTR((unused)))
{
}
#endif
/*
* Returns 0 if not able to find the quota file, otherwise returns its
* inode number.
*/
int quota_file_exists(ext2_filsys fs, enum quota_type qtype)
{
char qf_name[256];
errcode_t ret;
ext2_ino_t ino;
if (qtype >= MAXQUOTAS)
return -EINVAL;
quota_get_qf_name(qtype, QFMT_VFS_V1, qf_name);
ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0,
&ino);
if (ret)
return 0;
return ino;
}
/*
* Set the value for reserved quota inode number field in superblock.
*/
void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype)
{
ext2_ino_t *inump;
inump = quota_sb_inump(fs->super, qtype);
log_debug("setting quota ino in superblock: ino=%u, type=%d", ino,
qtype);
if (inump == NULL)
return;
*inump = ino;
ext2fs_mark_super_dirty(fs);
}
errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype)
{
ext2_ino_t qf_ino;
errcode_t retval;
retval = ext2fs_read_bitmaps(fs);
if (retval) {
log_debug("Couldn't read bitmaps: %s", error_message(retval));
return retval;
}
qf_ino = *quota_sb_inump(fs->super, qtype);
if (qf_ino == 0)
return 0;
retval = quota_inode_truncate(fs, qf_ino);
if (retval)
return retval;
if (qf_ino >= EXT2_FIRST_INODE(fs->super)) {
struct ext2_inode inode;
retval = ext2fs_read_inode(fs, qf_ino, &inode);
if (!retval) {
memset(&inode, 0, sizeof(struct ext2_inode));
ext2fs_write_inode(fs, qf_ino, &inode);
}
ext2fs_inode_alloc_stats2(fs, qf_ino, -1, 0);
ext2fs_mark_ib_dirty(fs);
}
quota_set_sb_inum(fs, 0, qtype);
ext2fs_mark_super_dirty(fs);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
retval = ext2fs_write_bitmaps(fs);
if (retval) {
log_debug("Couldn't write bitmaps: %s", error_message(retval));
return retval;
}
return 0;
}
static void write_dquots(dict_t *dict, struct quota_handle *qh)
{
dnode_t *n;
struct dquot *dq;
for (n = dict_first(dict); n; n = dict_next(dict, n)) {
dq = dnode_get(n);
if (dq) {
print_dquot("write", dq);
dq->dq_h = qh;
update_grace_times(dq);
qh->qh_ops->commit_dquot(dq);
}
}
}
errcode_t quota_write_inode(quota_ctx_t qctx, unsigned int qtype_bits)
{
int retval = 0;
enum quota_type qtype;
dict_t *dict;
ext2_filsys fs;
struct quota_handle *h = NULL;
int fmt = QFMT_VFS_V1;
if (!qctx)
return 0;
fs = qctx->fs;
retval = ext2fs_get_mem(sizeof(struct quota_handle), &h);
if (retval) {
log_debug("Unable to allocate quota handle: %s",
error_message(retval));
goto out;
}
retval = ext2fs_read_bitmaps(fs);
if (retval) {
log_debug("Couldn't read bitmaps: %s", error_message(retval));
goto out;
}
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
if (((1 << qtype) & qtype_bits) == 0)
continue;
dict = qctx->quota_dict[qtype];
if (!dict)
continue;
retval = quota_file_create(h, fs, qtype, fmt);
if (retval) {
log_debug("Cannot initialize io on quotafile: %s",
error_message(retval));
goto out;
}
write_dquots(dict, h);
retval = quota_file_close(qctx, h);
if (retval) {
log_debug("Cannot finish IO on new quotafile: %s",
strerror(errno));
if (h->qh_qf.e2_file)
ext2fs_file_close(h->qh_qf.e2_file);
(void) quota_inode_truncate(fs, h->qh_qf.ino);
goto out;
}
/* Set quota inode numbers in superblock. */
quota_set_sb_inum(fs, h->qh_qf.ino, qtype);
ext2fs_mark_super_dirty(fs);
ext2fs_mark_bb_dirty(fs);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
}
retval = ext2fs_write_bitmaps(fs);
if (retval) {
log_debug("Couldn't write bitmaps: %s", error_message(retval));
goto out;
}
out:
if (h)
ext2fs_free_mem(&h);
return retval;
}
/******************************************************************/
/* Helper functions for computing quota in memory. */
/******************************************************************/
static int dict_uint_cmp(const void *cmp_ctx EXT2FS_ATTR((unused)),
const void *a, const void *b)
{
unsigned int c, d;
c = VOIDPTR_TO_UINT(a);
d = VOIDPTR_TO_UINT(b);
if (c == d)
return 0;
else if (c > d)
return 1;
else
return -1;
}
static inline int project_quota_valid(quota_ctx_t qctx)
{
return (EXT2_INODE_SIZE(qctx->fs->super) > EXT2_GOOD_OLD_INODE_SIZE);
}
static inline qid_t get_qid(struct ext2_inode_large *inode, enum quota_type qtype)
{
unsigned int inode_size;
switch (qtype) {
case USRQUOTA:
return inode_uid(*inode);
case GRPQUOTA:
return inode_gid(*inode);
case PRJQUOTA:
inode_size = EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize;
if (inode_includes(inode_size, i_projid))
return inode_projid(*inode);
return 0;
default:
return 0;
}
return 0;
}
static void quota_dnode_free(dnode_t *node,
void *context EXT2FS_ATTR((unused)))
{
void *ptr = node ? dnode_get(node) : 0;
ext2fs_free_mem(&ptr);
free(node);
}
/*
* Set up the quota tracking data structures.
*/
errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs,
unsigned int qtype_bits)
{
errcode_t err;
dict_t *dict;
quota_ctx_t ctx;
enum quota_type qtype;
err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx);
if (err) {
log_debug("Failed to allocate quota context");
return err;
}
memset(ctx, 0, sizeof(struct quota_ctx));
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
ctx->quota_file[qtype] = NULL;
if (qtype_bits) {
if (((1 << qtype) & qtype_bits) == 0)
continue;
} else {
if (*quota_sb_inump(fs->super, qtype) == 0)
continue;
}
err = ext2fs_get_mem(sizeof(dict_t), &dict);
if (err) {
log_debug("Failed to allocate dictionary");
quota_release_context(&ctx);
return err;
}
ctx->quota_dict[qtype] = dict;
dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
}
ctx->fs = fs;
*qctx = ctx;
return 0;
}
void quota_release_context(quota_ctx_t *qctx)
{
errcode_t err;
dict_t *dict;
enum quota_type qtype;
quota_ctx_t ctx;
if (!qctx)
return;
ctx = *qctx;
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
dict = ctx->quota_dict[qtype];
ctx->quota_dict[qtype] = 0;
if (dict) {
dict_free_nodes(dict);
free(dict);
}
if (ctx->quota_file[qtype]) {
err = quota_file_close(ctx, ctx->quota_file[qtype]);
if (err) {
log_err("Cannot close quotafile: %s",
strerror(errno));
ext2fs_free_mem(&ctx->quota_file[qtype]);
}
}
}
*qctx = NULL;
free(ctx);
}
static struct dquot *get_dq(dict_t *dict, __u32 key)
{
struct dquot *dq;
dnode_t *n;
n = dict_lookup(dict, UINT_TO_VOIDPTR(key));
if (n)
dq = dnode_get(n);
else {
if (ext2fs_get_mem(sizeof(struct dquot), &dq)) {
log_err("Unable to allocate dquot");
return NULL;
}
memset(dq, 0, sizeof(struct dquot));
dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq);
dq->dq_id = key;
}
return dq;
}
/*
* Called to update the blocks used by a particular inode
*/
void quota_data_add(quota_ctx_t qctx, struct ext2_inode_large *inode,
ext2_ino_t ino EXT2FS_ATTR((unused)),
qsize_t space)
{
struct dquot *dq;
dict_t *dict;
enum quota_type qtype;
if (!qctx)
return;
log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
inode_uid(*inode),
inode_gid(*inode), space);
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
if (qtype == PRJQUOTA && !project_quota_valid(qctx))
continue;
dict = qctx->quota_dict[qtype];
if (dict) {
dq = get_dq(dict, get_qid(inode, qtype));
if (dq)
dq->dq_dqb.dqb_curspace += space;
}
}
}
/*
* Called to remove some blocks used by a particular inode
*/
void quota_data_sub(quota_ctx_t qctx, struct ext2_inode_large *inode,
ext2_ino_t ino EXT2FS_ATTR((unused)),
qsize_t space)
{
struct dquot *dq;
dict_t *dict;
enum quota_type qtype;
if (!qctx)
return;
log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
inode_uid(*inode),
inode_gid(*inode), space);
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
if (qtype == PRJQUOTA && !project_quota_valid(qctx))
continue;
dict = qctx->quota_dict[qtype];
if (dict) {
dq = get_dq(dict, get_qid(inode, qtype));
if (dq)
dq->dq_dqb.dqb_curspace -= space;
}
}
}
/*
* Called to count the files used by an inode's user/group
*/
void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode_large *inode,
ext2_ino_t ino EXT2FS_ATTR((unused)), int adjust)
{
struct dquot *dq;
dict_t *dict;
enum quota_type qtype;
if (!qctx)
return;
log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino,
inode_uid(*inode),
inode_gid(*inode), adjust);
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
if (qtype == PRJQUOTA && !project_quota_valid(qctx))
continue;
dict = qctx->quota_dict[qtype];
if (dict) {
dq = get_dq(dict, get_qid(inode, qtype));
if (dq)
dq->dq_dqb.dqb_curinodes += adjust;
}
}
}
errcode_t quota_compute_usage(quota_ctx_t qctx)
{
ext2_filsys fs;
ext2_ino_t ino;
errcode_t ret;
struct ext2_inode_large *inode;
int inode_size;
qsize_t space;
ext2_inode_scan scan;
if (!qctx)
return 0;
fs = qctx->fs;
ret = ext2fs_open_inode_scan(fs, 0, &scan);
if (ret) {
log_err("while opening inode scan. ret=%ld", ret);
return ret;
}
inode_size = fs->super->s_inode_size;
inode = malloc(inode_size);
if (!inode) {
ext2fs_close_inode_scan(scan);
return ENOMEM;
}
while (1) {
ret = ext2fs_get_next_inode_full(scan, &ino,
EXT2_INODE(inode), inode_size);
if (ret) {
log_err("while getting next inode. ret=%ld", ret);
ext2fs_close_inode_scan(scan);
free(inode);
return ret;
}
if (ino == 0)
break;
if (!inode->i_links_count)
continue;
if (ino == EXT2_ROOT_INO ||
(ino >= EXT2_FIRST_INODE(fs->super) &&
ino != quota_type2inum(PRJQUOTA, fs->super))) {
space = ext2fs_get_stat_i_blocks(fs,
EXT2_INODE(inode)) << 9;
quota_data_add(qctx, inode, ino, space);
quota_data_inodes(qctx, inode, ino, +1);
}
}
ext2fs_close_inode_scan(scan);
free(inode);
return 0;
}
struct scan_dquots_data {
dict_t *quota_dict;
int update_limits; /* update limits from disk */
int update_usage;
int check_consistency;
int usage_is_inconsistent;
};
static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
{
struct scan_dquots_data *scan_data = cb_data;
dict_t *quota_dict = scan_data->quota_dict;
struct dquot *dq;
dq = get_dq(quota_dict, dquot->dq_id);
if (!dq)
return -1;
dq->dq_id = dquot->dq_id;
dq->dq_flags |= DQF_SEEN;
print_dquot("mem", dq);
print_dquot("dsk", dquot);
/* Check if there is inconsistency */
if (scan_data->check_consistency &&
(dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes)) {
scan_data->usage_is_inconsistent = 1;
fprintf(stderr, "[QUOTA WARNING] Usage inconsistent for ID %u:"
"actual (%lld, %lld) != expected (%lld, %lld)\n",
dq->dq_id, (long long) dq->dq_dqb.dqb_curspace,
(long long) dq->dq_dqb.dqb_curinodes,
(long long) dquot->dq_dqb.dqb_curspace,
(long long) dquot->dq_dqb.dqb_curinodes);
}
if (scan_data->update_limits) {
dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
}
if (scan_data->update_usage) {
dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
}
return 0;
}
/*
* Read quotas from disk and updates the in-memory information determined by
* 'flags' from the on-disk data.
*/
errcode_t quota_read_all_dquots(quota_ctx_t qctx, ext2_ino_t qf_ino,
enum quota_type qtype, unsigned int flags)
{
struct scan_dquots_data scan_data;
struct quota_handle *qh;
errcode_t err;
if (!qctx)
return 0;
err = ext2fs_get_mem(sizeof(struct quota_handle), &qh);
if (err) {
log_debug("Unable to allocate quota handle");
return err;
}
err = quota_file_open(qctx, qh, qf_ino, qtype, -1, 0);
if (err) {
log_debug("Open quota file failed");
goto out;
}
scan_data.quota_dict = qctx->quota_dict[qh->qh_type];
scan_data.check_consistency = 0;
scan_data.update_limits = !!(flags & QREAD_LIMITS);
scan_data.update_usage = !!(flags & QREAD_USAGE);
qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data);
err = quota_file_close(qctx, qh);
if (err) {
log_debug("Cannot finish IO on new quotafile: %s",
strerror(errno));
if (qh->qh_qf.e2_file)
ext2fs_file_close(qh->qh_qf.e2_file);
}
out:
ext2fs_free_mem(&qh);
return err;
}
/*
* Compares the measured quota in qctx->quota_dict with that in the quota inode
* on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
* set to 1 if the supplied and on-disk quota usage values are not identical.
*/
errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
int *usage_inconsistent)
{
struct quota_handle qh;
struct scan_dquots_data scan_data;
struct dquot *dq;
dnode_t *n;
dict_t *dict = qctx->quota_dict[qtype];
errcode_t err = 0;
if (!dict)
goto out;
err = quota_file_open(qctx, &qh, 0, qtype, -1, 0);
if (err) {
log_debug("Open quota file failed");
goto out;
}
scan_data.quota_dict = qctx->quota_dict[qtype];
scan_data.update_limits = 1;
scan_data.update_usage = 0;
scan_data.check_consistency = 1;
scan_data.usage_is_inconsistent = 0;
err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
if (err) {
log_debug("Error scanning dquots");
*usage_inconsistent = 1;
goto out_close_qh;
}
for (n = dict_first(dict); n; n = dict_next(dict, n)) {
dq = dnode_get(n);
if (!dq)
continue;
if ((dq->dq_flags & DQF_SEEN) == 0) {
fprintf(stderr, "[QUOTA WARNING] "
"Missing quota entry ID %d\n", dq->dq_id);
scan_data.usage_is_inconsistent = 1;
}
}
*usage_inconsistent = scan_data.usage_is_inconsistent;
out_close_qh:
err = quota_file_close(qctx, &qh);
if (err) {
log_debug("Cannot close quotafile: %s", error_message(errno));
if (qh.qh_qf.e2_file)
ext2fs_file_close(qh.qh_qf.e2_file);
}
out:
return err;
}
int parse_quota_opts(const char *opts, int (*func)(char *))
{
char *buf, *token, *next, *p;
int len;
int ret = 0;
len = strlen(opts);
buf = malloc(len + 1);
if (!buf) {
fprintf(stderr,
"Couldn't allocate memory to parse quota options!\n");
return -ENOMEM;
}
strcpy(buf, opts);
for (token = buf; token && *token; token = next) {
p = strchr(token, ',');
next = 0;
if (p) {
*p = 0;
next = p + 1;
}
ret = func(token);
if (ret)
break;
}
free(buf);
return ret;
}

View File

@@ -0,0 +1,90 @@
/*
* parse_qtype.c
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include "quotaio.h"
#define PARSE_DELIM ":,"
int parse_quota_types(const char *in_str, unsigned int *qtype_bits,
char **err_token)
{
char *buf, *token, *next, *tmp;
unsigned int qtype = *qtype_bits;
int len, ret = 0;
if (!in_str)
return 0;
len = strlen(in_str);
buf = malloc(len + 1);
if (!buf)
return ENOMEM;
strcpy(buf, in_str);
for (token = buf, next = strtok_r(buf, PARSE_DELIM, &tmp);
token && *token; token = next) {
int not = 0;
char *p = token;
if (*p == '^') {
not = 1;
p++;
}
if (!strcmp(p, "usr") || !strcmp(p, "usrquota")) {
if (not)
qtype &= ~QUOTA_USR_BIT;
else
qtype |= QUOTA_USR_BIT;
} else if (!strcmp(p, "grp") || !strcmp(p, "grpquota")) {
if (not)
qtype &= ~QUOTA_GRP_BIT;
else
qtype |= QUOTA_GRP_BIT;
} else if (!strcmp(p, "prj") || !strcmp(p, "prjquota")) {
if (not)
qtype &= ~QUOTA_PRJ_BIT;
else
qtype |= QUOTA_PRJ_BIT;
} else {
if (err_token) {
*err_token = malloc(strlen(token) + 1);
if (*err_token)
strcpy(*err_token, token);
}
ret = EINVAL;
goto errout;
}
#ifdef DEBUG_PROGRAM
printf("word: %s\n", token);
#endif
next = strtok_r(NULL, PARSE_DELIM, &tmp);
}
*qtype_bits = qtype;
errout:
free(buf);
return ret;
}
#ifdef DEBUG_PROGRAM
int main(int argc, char **argv)
{
unsigned int qtype_bits = 0;
int ret;
char *err_token = 0;
ret = parse_quota_types(argv[1], &qtype_bits, &err_token);
printf("parse_quota_types returns %d, %d\n", ret, qtype_bits);
if (err_token)
printf("err_token is %s\n", err_token);
return 0;
}
#endif

View File

@@ -0,0 +1,287 @@
/*
* plausible.c --- Figure out if a pathname is ext* or something else.
*
* Copyright 2014, Oracle, Inc.
*
* Some parts are:
* Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*/
#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#include "config.h"
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_MAGIC_H
#include <magic.h>
#endif
#include "plausible.h"
#include "ext2fs/ext2fs.h"
#include "nls-enable.h"
#include "blkid/blkid.h"
#ifdef HAVE_MAGIC_H
static magic_t (*dl_magic_open)(int);
static const char *(*dl_magic_file)(magic_t, const char *);
static int (*dl_magic_load)(magic_t, const char *);
static void (*dl_magic_close)(magic_t);
/*
* NO_CHECK functionality was only added in file 4.20.
* Older systems like RHEL 5.x still have file 4.17
*/
#ifndef MAGIC_NO_CHECK_COMPRESS
#define MAGIC_NO_CHECK_COMPRESS 0x0001000
#endif
#ifndef MAGIC_NO_CHECK_ELF
#define MAGIC_NO_CHECK_ELF 0x0010000
#endif
#ifdef HAVE_DLOPEN
#include <dlfcn.h>
static void *magic_handle;
static int magic_library_available(void)
{
if (!magic_handle) {
magic_handle = dlopen("libmagic.so.1", RTLD_NOW);
if (!magic_handle)
return 0;
dl_magic_open = (magic_t (*)(int))
dlsym(magic_handle, "magic_open");
dl_magic_file = (const char *(*)(magic_t, const char *))
dlsym(magic_handle, "magic_file");
dl_magic_load = (int (*)(magic_t, const char *))
dlsym(magic_handle, "magic_load");
dl_magic_close = (void (*)(magic_t))
dlsym(magic_handle, "magic_close");
}
if (!dl_magic_open || !dl_magic_file ||
!dl_magic_load || !dl_magic_close)
return 0;
return 1;
}
#else
static int magic_library_available(void)
{
dl_magic_open = magic_open;
dl_magic_file = magic_file;
dl_magic_load = magic_load;
dl_magic_close = magic_close;
return 1;
}
#endif
#endif
static void print_ext2_info(const char *device)
{
struct ext2_super_block *sb;
ext2_filsys fs;
errcode_t retval;
time_t tm;
retval = ext2fs_open2(device, 0, EXT2_FLAG_64BITS, 0, 0,
default_io_manager, &fs);
if (retval)
return;
sb = fs->super;
if (sb->s_mtime) {
tm = sb->s_mtime;
if (sb->s_last_mounted[0])
printf(_("\tlast mounted on %.*s on %s"),
EXT2_LEN_STR(sb->s_last_mounted), ctime(&tm));
else
printf(_("\tlast mounted on %s"), ctime(&tm));
} else if (sb->s_mkfs_time) {
tm = sb->s_mkfs_time;
printf(_("\tcreated on %s"), ctime(&tm));
} else if (sb->s_wtime) {
tm = sb->s_wtime;
printf(_("\tlast modified on %s"), ctime(&tm));
}
ext2fs_close_free(&fs);
}
/*
* return 1 if there is no partition table, 0 if a partition table is
* detected, and -1 on an error.
*/
#ifdef HAVE_BLKID_PROBE_ENABLE_PARTITIONS
static int check_partition_table(const char *device)
{
blkid_probe pr;
const char *value;
int ret;
pr = blkid_new_probe_from_filename(device);
if (!pr)
return -1;
ret = blkid_probe_enable_partitions(pr, 1);
if (ret < 0)
goto errout;
ret = blkid_probe_enable_superblocks(pr, 0);
if (ret < 0)
goto errout;
ret = blkid_do_fullprobe(pr);
if (ret < 0)
goto errout;
ret = blkid_probe_lookup_value(pr, "PTTYPE", &value, NULL);
if (ret == 0)
fprintf(stderr, _("Found a %s partition table in %s\n"),
value, device);
else
ret = 1;
errout:
blkid_free_probe(pr);
return ret;
}
#else
static int check_partition_table(const char *device EXT2FS_ATTR((unused)))
{
return -1;
}
#endif
/*
* return 1 if the device looks plausible, creating the file if necessary
*/
int check_plausibility(const char *device, int flags, int *ret_is_dev)
{
int fd, ret, is_dev = 0;
ext2fs_struct_stat s;
int fl = O_RDONLY;
blkid_cache cache = NULL;
char *fs_type = NULL;
char *fs_label = NULL;
fd = ext2fs_open_file(device, fl, 0666);
if ((fd < 0) && (errno == ENOENT) && (flags & NO_SIZE)) {
fprintf(stderr, _("The file %s does not exist and no "
"size was specified.\n"), device);
exit(1);
}
if ((fd < 0) && (errno == ENOENT) && (flags & CREATE_FILE)) {
fl |= O_CREAT;
fd = ext2fs_open_file(device, fl, 0666);
if (fd >= 0 && (flags & VERBOSE_CREATE))
printf(_("Creating regular file %s\n"), device);
}
if (fd < 0) {
fprintf(stderr, _("Could not open %s: %s\n"),
device, error_message(errno));
if (errno == ENOENT)
fputs(_("\nThe device apparently does not exist; "
"did you specify it correctly?\n"), stderr);
exit(1);
}
if (ext2fs_fstat(fd, &s) < 0) {
perror("stat");
exit(1);
}
close(fd);
if (S_ISBLK(s.st_mode))
is_dev = 1;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
/* On FreeBSD, all disk devices are character specials */
if (S_ISCHR(s.st_mode))
is_dev = 1;
#endif
if (ret_is_dev)
*ret_is_dev = is_dev;
if ((flags & CHECK_BLOCK_DEV) && !is_dev) {
printf(_("%s is not a block special device.\n"), device);
return 0;
}
/*
* Note: we use the older-style blkid API's here because we
* want as much functionality to be available when using the
* internal blkid library, when e2fsprogs is compiled for
* non-Linux systems that will probably not have the libraries
* from util-linux available. We only use the newer
* blkid-probe interfaces to access functionality not
* available in the original blkid library.
*/
if ((flags & CHECK_FS_EXIST) && blkid_get_cache(&cache, NULL) >= 0) {
fs_type = blkid_get_tag_value(cache, "TYPE", device);
if (fs_type)
fs_label = blkid_get_tag_value(cache, "LABEL", device);
blkid_put_cache(cache);
}
if (fs_type) {
if (fs_label)
printf(_("%s contains a %s file system labelled '%s'\n"),
device, fs_type, fs_label);
else
printf(_("%s contains a %s file system\n"), device,
fs_type);
if (strncmp(fs_type, "ext", 3) == 0)
print_ext2_info(device);
free(fs_type);
free(fs_label);
return 0;
}
#ifdef HAVE_MAGIC_H
if ((flags & CHECK_FS_EXIST) &&
!getenv("E2FSPROGS_LIBMAGIC_SUPPRESS") &&
magic_library_available()) {
const char *msg;
magic_t mag;
int has_magic = 0;
mag = dl_magic_open(MAGIC_RAW | MAGIC_SYMLINK | MAGIC_DEVICES |
MAGIC_ERROR | MAGIC_NO_CHECK_ELF |
MAGIC_NO_CHECK_COMPRESS);
dl_magic_load(mag, NULL);
msg = dl_magic_file(mag, device);
if (msg && strcmp(msg, "data") && strcmp(msg, "empty")) {
printf(_("%s contains `%s' data\n"), device, msg);
has_magic = 1;
}
dl_magic_close(mag);
return !has_magic;
}
#endif
if (flags & CHECK_FS_EXIST) {
ret = check_partition_table(device);
if (ret >= 0)
return ret;
}
return 1;
}

View File

@@ -0,0 +1,85 @@
/*
* prof_err.c:
* This file is automatically generated; please do not edit it.
*/
#include <stdlib.h>
#define N_(a) a
static const char * const text[] = {
N_("Profile version 0.0"),
N_("Bad magic value in profile_node"),
N_("Profile section not found"),
N_("Profile relation not found"),
N_( "Attempt to add a relation to node which is not a section"),
N_( "A profile section header has a non-zero value"),
N_("Bad linked list in profile structures"),
N_("Bad group level in profile structures"),
N_( "Bad parent pointer in profile structures"),
N_("Bad magic value in profile iterator"),
N_("Can't set value on section node"),
N_("Invalid argument passed to profile library"),
N_("Attempt to modify read-only profile"),
N_("Profile section header not at top level"),
N_("Syntax error in profile section header"),
N_("Syntax error in profile relation"),
N_("Extra closing brace in profile"),
N_("Missing open brace in profile"),
N_("Bad magic value in profile_t"),
N_("Bad magic value in profile_section_t"),
N_( "Iteration through all top level section not supported"),
N_("Invalid profile_section object"),
N_("No more sections"),
N_("Bad nameset passed to query routine"),
N_("No profile file open"),
N_("Bad magic value in profile_file_t"),
N_("Couldn't open profile file"),
N_("Section already exists"),
N_("Invalid boolean value"),
N_("Invalid integer value"),
N_("Bad magic value in profile_file_data_t"),
0
};
struct error_table {
char const * const * msgs;
long base;
int n_msgs;
};
struct et_list {
struct et_list *next;
const struct error_table * table;
};
extern struct et_list *_et_list;
const struct error_table et_prof_error_table = { text, -1429577728L, 31 };
static struct et_list link = { 0, 0 };
void initialize_prof_error_table_r(struct et_list **list);
void initialize_prof_error_table(void);
void initialize_prof_error_table(void) {
initialize_prof_error_table_r(&_et_list);
}
/* For Heimdal compatibility */
void initialize_prof_error_table_r(struct et_list **list)
{
struct et_list *et, **end;
for (end = list, et = *list; et; end = &et->next, et = et->next)
if (et->table->msgs == text)
return;
et = malloc(sizeof(struct et_list));
if (et == 0) {
if (!link.table)
et = &link;
else
return;
}
et->table = &et_prof_error_table;
et->next = 0;
*end = et;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,317 @@
/*
* profile_helpers.c -- Helper functions for the profile library
*
* These functions are not part of the "core" profile library, and do
* not require access to the internal functions and data structures of
* the profile library. They are mainly convenience functions for
* programs that want to do something unusual such as obtaining the
* list of sections or relations, or accessing multiple values from a
* relation that is listed more than once. This functionality can all
* be done using the profile_iterator abstraction, but it is less
* convenient.
*
* Copyright (C) 2006 by Theodore Ts'o.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <et/com_err.h>
#include "profile.h"
#include "profile_helpers.h"
#include "prof_err.h"
/*
* These functions --- init_list(), end_list(), and add_to_list() are
* internal functions used to build up a null-terminated char ** list
* of strings to be returned by functions like profile_get_values.
*
* The profile_string_list structure is used for internal booking
* purposes to build up the list, which is returned in *ret_list by
* the end_list() function.
*
* The publicly exported interface for freeing char** list is
* profile_free_list().
*/
struct profile_string_list {
char **list;
int num;
int max;
};
/*
* Initialize the string list abstraction.
*/
static errcode_t init_list(struct profile_string_list *list)
{
list->num = 0;
list->max = 10;
list->list = malloc(list->max * sizeof(char *));
if (list->list == 0)
return ENOMEM;
list->list[0] = 0;
return 0;
}
/*
* Free any memory left over in the string abstraction, returning the
* built up list in *ret_list if it is non-null.
*/
static void end_list(struct profile_string_list *list, char ***ret_list)
{
char **cp;
if (list == 0)
return;
if (ret_list) {
*ret_list = list->list;
return;
} else {
for (cp = list->list; *cp; cp++)
free(*cp);
free(list->list);
}
list->num = list->max = 0;
list->list = 0;
}
/*
* Add a string to the list.
*/
static errcode_t add_to_list(struct profile_string_list *list, char *str)
{
char **newlist;
int newmax;
if (list->num+1 >= list->max) {
newmax = list->max + 10;
newlist = realloc(list->list, newmax * sizeof(char *));
if (newlist == 0)
return ENOMEM;
list->max = newmax;
list->list = newlist;
}
list->list[list->num++] = str;
list->list[list->num] = 0;
return 0;
}
/*
* Return TRUE if the string is already a member of the list.
*/
static int is_list_member(struct profile_string_list *list, const char *str)
{
char **cpp;
if (!list->list)
return 0;
for (cpp = list->list; *cpp; cpp++) {
if (!strcmp(*cpp, str))
return 1;
}
return 0;
}
/*
* This function frees a null-terminated list as returned by
* profile_get_values.
*/
void profile_free_list(char **list)
{
char **cp;
if (list == 0)
return;
for (cp = list; *cp; cp++)
free(*cp);
free(list);
}
errcode_t
profile_get_values(profile_t profile, const char *const *names,
char ***ret_values)
{
errcode_t retval;
void *state;
char *value;
struct profile_string_list values;
if ((retval = profile_iterator_create(profile, names,
PROFILE_ITER_RELATIONS_ONLY,
&state)))
return retval;
if ((retval = init_list(&values)))
goto cleanup_iterator;
do {
if ((retval = profile_iterator(&state, 0, &value)))
goto cleanup;
if (value)
add_to_list(&values, value);
} while (state);
if (values.num == 0) {
retval = PROF_NO_RELATION;
goto cleanup;
}
end_list(&values, ret_values);
return 0;
cleanup:
end_list(&values, 0);
cleanup_iterator:
profile_iterator_free(&state);
return retval;
}
/*
* This function will return the list of the names of subsections in the
* under the specified section name.
*/
errcode_t
profile_get_subsection_names(profile_t profile, const char **names,
char ***ret_names)
{
errcode_t retval;
void *state;
char *name;
struct profile_string_list values;
if ((retval = profile_iterator_create(profile, names,
PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY,
&state)))
return retval;
if ((retval = init_list(&values)))
goto cleanup_iterator;
do {
if ((retval = profile_iterator(&state, &name, 0)))
goto cleanup;
if (name)
add_to_list(&values, name);
} while (state);
end_list(&values, ret_names);
return 0;
cleanup:
end_list(&values, 0);
cleanup_iterator:
profile_iterator_free(&state);
return retval;
}
/*
* This function will return the list of the names of relations in the
* under the specified section name.
*/
errcode_t
profile_get_relation_names(profile_t profile, const char **names,
char ***ret_names)
{
errcode_t retval;
void *state;
char *name;
struct profile_string_list values;
if ((retval = profile_iterator_create(profile, names,
PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY,
&state)))
return retval;
if ((retval = init_list(&values)))
goto cleanup_iterator;
do {
if ((retval = profile_iterator(&state, &name, 0)))
goto cleanup;
if (name) {
if (is_list_member(&values, name))
free(name);
else
add_to_list(&values, name);
}
} while (state);
end_list(&values, ret_names);
return 0;
cleanup:
end_list(&values, 0);
cleanup_iterator:
profile_iterator_free(&state);
return retval;
}
void
profile_release_string(char *str)
{
free(str);
}
errcode_t
profile_init_path(const char * filepath,
profile_t *ret_profile)
{
int n_entries, i;
unsigned int ent_len;
const char *s, *t;
char **filenames;
errcode_t retval;
/* count the distinct filename components */
for(s = filepath, n_entries = 1; *s; s++) {
if (*s == ':')
n_entries++;
}
/* the array is NULL terminated */
filenames = (char **) malloc((n_entries+1) * sizeof(char*));
if (filenames == 0)
return ENOMEM;
/* measure, copy, and skip each one */
for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) {
ent_len = t-s;
filenames[i] = (char*) malloc(ent_len + 1);
if (filenames[i] == 0) {
/* if malloc fails, free the ones that worked */
while(--i >= 0) free(filenames[i]);
free(filenames);
return ENOMEM;
}
strncpy(filenames[i], s, ent_len);
filenames[i][ent_len] = 0;
if (*t == 0) {
i++;
break;
}
}
/* cap the array */
filenames[i] = 0;
retval = profile_init((const char * const *) filenames,
ret_profile);
/* count back down and free the entries */
while(--i >= 0) free(filenames[i]);
free(filenames);
return retval;
}

View File

@@ -0,0 +1,412 @@
/** quotaio.c
*
* Generic IO operations on quotafiles
* Jan Kara <jack@suse.cz> - sponsored by SuSE CR
* Aditya Kali <adityakali@google.com> - Ported to e2fsprogs
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <assert.h>
#include "common.h"
#include "quotaio.h"
static const char * const extensions[MAXQUOTAS] = {
[USRQUOTA] = "user",
[GRPQUOTA] = "group",
[PRJQUOTA] = "project",
};
static const char * const basenames[] = {
"", /* undefined */
"quota", /* QFMT_VFS_OLD */
"aquota", /* QFMT_VFS_V0 */
"", /* QFMT_OCFS2 */
"aquota" /* QFMT_VFS_V1 */
};
/* Header in all newer quotafiles */
struct disk_dqheader {
__le32 dqh_magic;
__le32 dqh_version;
} __attribute__ ((packed));
/**
* Convert type of quota to written representation
*/
const char *quota_type2name(enum quota_type qtype)
{
if (qtype >= MAXQUOTAS)
return "unknown";
return extensions[qtype];
}
ext2_ino_t quota_type2inum(enum quota_type qtype,
struct ext2_super_block *sb)
{
switch (qtype) {
case USRQUOTA:
return EXT4_USR_QUOTA_INO;
case GRPQUOTA:
return EXT4_GRP_QUOTA_INO;
case PRJQUOTA:
return sb->s_prj_quota_inum;
default:
return 0;
}
return 0;
}
/**
* Creates a quota file name for given type and format.
*/
const char *quota_get_qf_name(enum quota_type type, int fmt, char *buf)
{
if (!buf)
return NULL;
snprintf(buf, QUOTA_NAME_LEN, "%s.%s",
basenames[fmt], extensions[type]);
return buf;
}
/*
* Set grace time if needed
*/
void update_grace_times(struct dquot *q)
{
time_t now;
time(&now);
if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) >
q->dq_dqb.dqb_bsoftlimit) {
if (!q->dq_dqb.dqb_btime)
q->dq_dqb.dqb_btime =
now + q->dq_h->qh_info.dqi_bgrace;
} else {
q->dq_dqb.dqb_btime = 0;
}
if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes >
q->dq_dqb.dqb_isoftlimit) {
if (!q->dq_dqb.dqb_itime)
q->dq_dqb.dqb_itime =
now + q->dq_h->qh_info.dqi_igrace;
} else {
q->dq_dqb.dqb_itime = 0;
}
}
errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino)
{
struct ext2_inode inode;
errcode_t err;
enum quota_type qtype;
if ((err = ext2fs_read_inode(fs, ino, &inode)))
return err;
for (qtype = 0; qtype < MAXQUOTAS; qtype++)
if (ino == quota_type2inum(qtype, fs->super))
break;
if (qtype != MAXQUOTAS) {
inode.i_dtime = fs->now ? fs->now : time(0);
if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
return 0;
err = ext2fs_punch(fs, ino, &inode, NULL, 0, ~0ULL);
if (err)
return err;
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
memset(&inode, 0, sizeof(struct ext2_inode));
} else {
inode.i_flags &= ~EXT2_IMMUTABLE_FL;
}
err = ext2fs_write_inode(fs, ino, &inode);
return err;
}
/* Functions to read/write quota file. */
static unsigned int quota_write_nomount(struct quota_file *qf,
ext2_loff_t offset,
void *buf, unsigned int size)
{
ext2_file_t e2_file = qf->e2_file;
unsigned int bytes_written = 0;
errcode_t err;
err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL);
if (err) {
log_err("ext2fs_file_llseek failed: %ld", err);
return 0;
}
err = ext2fs_file_write(e2_file, buf, size, &bytes_written);
if (err) {
log_err("ext2fs_file_write failed: %ld", err);
return 0;
}
/* Correct inode.i_size is set in end_io. */
return bytes_written;
}
static unsigned int quota_read_nomount(struct quota_file *qf,
ext2_loff_t offset,
void *buf, unsigned int size)
{
ext2_file_t e2_file = qf->e2_file;
unsigned int bytes_read = 0;
errcode_t err;
err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL);
if (err) {
log_err("ext2fs_file_llseek failed: %ld", err);
return 0;
}
err = ext2fs_file_read(e2_file, buf, size, &bytes_read);
if (err) {
log_err("ext2fs_file_read failed: %ld", err);
return 0;
}
return bytes_read;
}
/*
* Detect quota format and initialize quota IO
*/
errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
ext2_ino_t qf_ino, enum quota_type qtype,
int fmt, int flags)
{
ext2_filsys fs = qctx->fs;
ext2_file_t e2_file;
errcode_t err;
int allocated_handle = 0;
if (qtype >= MAXQUOTAS)
return EINVAL;
if (fmt == -1)
fmt = QFMT_VFS_V1;
err = ext2fs_read_bitmaps(fs);
if (err)
return err;
if (qf_ino == 0)
qf_ino = *quota_sb_inump(fs->super, qtype);
log_debug("Opening quota ino=%u, type=%d", qf_ino, qtype);
err = ext2fs_file_open(fs, qf_ino, flags, &e2_file);
if (err) {
log_err("ext2fs_file_open failed: %s", error_message(err));
return err;
}
if (!h) {
if (qctx->quota_file[qtype]) {
h = qctx->quota_file[qtype];
if (((flags & EXT2_FILE_WRITE) == 0) ||
(h->qh_file_flags & EXT2_FILE_WRITE)) {
ext2fs_file_close(e2_file);
return 0;
}
(void) quota_file_close(qctx, h);
}
err = ext2fs_get_mem(sizeof(struct quota_handle), &h);
if (err) {
log_err("Unable to allocate quota handle");
ext2fs_file_close(e2_file);
return err;
}
allocated_handle = 1;
}
h->qh_qf.e2_file = e2_file;
h->qh_qf.fs = fs;
h->qh_qf.ino = qf_ino;
h->e2fs_write = quota_write_nomount;
h->e2fs_read = quota_read_nomount;
h->qh_file_flags = flags;
h->qh_io_flags = 0;
h->qh_type = qtype;
h->qh_fmt = fmt;
memset(&h->qh_info, 0, sizeof(h->qh_info));
h->qh_ops = &quotafile_ops_2;
if (h->qh_ops->check_file &&
(h->qh_ops->check_file(h, qtype, fmt) == 0)) {
log_err("qh_ops->check_file failed");
err = EIO;
goto errout;
}
if (h->qh_ops->init_io && (h->qh_ops->init_io(h) < 0)) {
log_err("qh_ops->init_io failed");
err = EIO;
goto errout;
}
if (allocated_handle)
qctx->quota_file[qtype] = h;
return 0;
errout:
ext2fs_file_close(e2_file);
if (allocated_handle)
ext2fs_free_mem(&h);
return err;
}
static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
{
struct ext2_inode inode;
errcode_t err = 0;
err = ext2fs_read_inode(fs, ino, &inode);
if (err) {
log_err("ex2fs_read_inode failed");
return err;
}
if (EXT2_I_SIZE(&inode)) {
err = quota_inode_truncate(fs, ino);
if (err)
return err;
}
memset(&inode, 0, sizeof(struct ext2_inode));
ext2fs_iblk_set(fs, &inode, 0);
inode.i_atime = inode.i_mtime =
inode.i_ctime = fs->now ? fs->now : time(0);
inode.i_links_count = 1;
inode.i_mode = LINUX_S_IFREG | 0600;
inode.i_flags |= EXT2_IMMUTABLE_FL;
if (ext2fs_has_feature_extents(fs->super))
inode.i_flags |= EXT4_EXTENTS_FL;
err = ext2fs_write_new_inode(fs, ino, &inode);
if (err) {
log_err("ext2fs_write_new_inode failed: %ld", err);
return err;
}
return err;
}
/*
* Create new quotafile of specified format on given filesystem
*/
errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
enum quota_type qtype, int fmt)
{
ext2_file_t e2_file;
errcode_t err;
ext2_ino_t qf_inum = 0;
if (fmt == -1)
fmt = QFMT_VFS_V1;
h->qh_qf.fs = fs;
qf_inum = quota_type2inum(qtype, fs->super);
if (qf_inum == 0 && qtype == PRJQUOTA) {
err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600,
0, &qf_inum);
if (err)
return err;
ext2fs_inode_alloc_stats2(fs, qf_inum, +1, 0);
ext2fs_mark_ib_dirty(fs);
} else if (qf_inum == 0) {
return EXT2_ET_BAD_INODE_NUM;
}
err = ext2fs_read_bitmaps(fs);
if (err)
goto out_err;
err = quota_inode_init_new(fs, qf_inum);
if (err) {
log_err("init_new_quota_inode failed");
goto out_err;
}
h->qh_qf.ino = qf_inum;
h->qh_file_flags = EXT2_FILE_WRITE | EXT2_FILE_CREATE;
h->e2fs_write = quota_write_nomount;
h->e2fs_read = quota_read_nomount;
log_debug("Creating quota ino=%u, type=%d", qf_inum, qtype);
err = ext2fs_file_open(fs, qf_inum, h->qh_file_flags, &e2_file);
if (err) {
log_err("ext2fs_file_open failed: %ld", err);
goto out_err;
}
h->qh_qf.e2_file = e2_file;
h->qh_io_flags = 0;
h->qh_type = qtype;
h->qh_fmt = fmt;
memset(&h->qh_info, 0, sizeof(h->qh_info));
h->qh_ops = &quotafile_ops_2;
if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) {
log_err("qh_ops->new_io failed");
err = EIO;
goto out_err1;
}
return 0;
out_err1:
ext2fs_file_close(e2_file);
out_err:
if (qf_inum)
quota_inode_truncate(fs, qf_inum);
return err;
}
/*
* Close quotafile and release handle
*/
errcode_t quota_file_close(quota_ctx_t qctx, struct quota_handle *h)
{
if (h->qh_io_flags & IOFL_INFODIRTY) {
if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
return EIO;
h->qh_io_flags &= ~IOFL_INFODIRTY;
}
if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
return EIO;
if (h->qh_qf.e2_file)
ext2fs_file_close(h->qh_qf.e2_file);
if (qctx->quota_file[h->qh_type] == h)
ext2fs_free_mem(&qctx->quota_file[h->qh_type]);
return 0;
}
/*
* Create empty quota structure
*/
struct dquot *get_empty_dquot(void)
{
struct dquot *dquot;
if (ext2fs_get_memzero(sizeof(struct dquot), &dquot)) {
log_err("Failed to allocate dquot");
return NULL;
}
dquot->dq_id = -1;
return dquot;
}

View File

@@ -0,0 +1,687 @@
/*
* Implementation of new quotafile format
*
* Jan Kara <jack@suse.cz> - sponsored by SuSE CR
*/
#include "config.h"
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "quotaio_tree.h"
#include "quotaio.h"
typedef char *dqbuf_t;
#define freedqbuf(buf) ext2fs_free_mem(&buf)
static inline dqbuf_t getdqbuf(void)
{
dqbuf_t buf;
if (ext2fs_get_memzero(QT_BLKSIZE, &buf)) {
log_err("Failed to allocate dqbuf");
return NULL;
}
return buf;
}
/* Is given dquot empty? */
int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
{
unsigned int i;
for (i = 0; i < info->dqi_entry_size; i++)
if (disk[i])
return 0;
return 1;
}
int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
{
return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) /
info->dqi_entry_size;
}
static int get_index(qid_t id, int depth)
{
return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
}
static inline void mark_quotafile_info_dirty(struct quota_handle *h)
{
h->qh_io_flags |= IOFL_INFODIRTY;
}
/* Read given block */
static void read_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
{
int err;
err = h->e2fs_read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
QT_BLKSIZE);
if (err < 0)
log_err("Cannot read block %u: %s", blk, strerror(errno));
else if (err != QT_BLKSIZE)
memset(buf + err, 0, QT_BLKSIZE - err);
}
/* Write block */
static int write_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
{
int err;
err = h->e2fs_write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
QT_BLKSIZE);
if (err < 0 && errno != ENOSPC)
log_err("Cannot write block (%u): %s", blk, strerror(errno));
if (err != QT_BLKSIZE)
return -ENOSPC;
return 0;
}
/* Get free block in file (either from free list or create new one) */
static int get_free_dqblk(struct quota_handle *h)
{
dqbuf_t buf = getdqbuf();
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
int blk;
if (!buf)
return -ENOMEM;
if (info->dqi_free_blk) {
blk = info->dqi_free_blk;
read_blk(h, blk, buf);
info->dqi_free_blk = ext2fs_le32_to_cpu(dh->dqdh_next_free);
} else {
memset(buf, 0, QT_BLKSIZE);
/* Assure block allocation... */
if (write_blk(h, info->dqi_blocks, buf) < 0) {
freedqbuf(buf);
log_err("Cannot allocate new quota block "
"(out of disk space).");
return -ENOSPC;
}
blk = info->dqi_blocks++;
}
mark_quotafile_info_dirty(h);
freedqbuf(buf);
return blk;
}
/* Put given block to free list */
static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf,
unsigned int blk)
{
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_blk);
dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
dh->dqdh_entries = ext2fs_cpu_to_le16(0);
info->dqi_free_blk = blk;
mark_quotafile_info_dirty(h);
write_blk(h, blk, buf);
}
/* Remove given block from the list of blocks with free entries */
static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf,
unsigned int blk)
{
dqbuf_t tmpbuf = getdqbuf();
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
unsigned int nextblk = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk =
ext2fs_le32_to_cpu(dh->dqdh_prev_free);
if (!tmpbuf)
return;
if (nextblk) {
read_blk(h, nextblk, tmpbuf);
((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
dh->dqdh_prev_free;
write_blk(h, nextblk, tmpbuf);
}
if (prevblk) {
read_blk(h, prevblk, tmpbuf);
((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
dh->dqdh_next_free;
write_blk(h, prevblk, tmpbuf);
} else {
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
mark_quotafile_info_dirty(h);
}
freedqbuf(tmpbuf);
dh->dqdh_next_free = dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
write_blk(h, blk, buf); /* No matter whether write succeeds
* block is out of list */
}
/* Insert given block to the beginning of list with free entries */
static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf,
unsigned int blk)
{
dqbuf_t tmpbuf = getdqbuf();
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
if (!tmpbuf)
return;
dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_entry);
dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
write_blk(h, blk, buf);
if (info->dqi_free_entry) {
read_blk(h, info->dqi_free_entry, tmpbuf);
((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
ext2fs_cpu_to_le32(blk);
write_blk(h, info->dqi_free_entry, tmpbuf);
}
freedqbuf(tmpbuf);
info->dqi_free_entry = blk;
mark_quotafile_info_dirty(h);
}
/* Find space for dquot */
static unsigned int find_free_dqentry(struct quota_handle *h,
struct dquot *dquot, int *err)
{
int blk, i;
struct qt_disk_dqdbheader *dh;
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
char *ddquot;
dqbuf_t buf;
*err = 0;
buf = getdqbuf();
if (!buf) {
*err = -ENOMEM;
return 0;
}
dh = (struct qt_disk_dqdbheader *)buf;
if (info->dqi_free_entry) {
blk = info->dqi_free_entry;
read_blk(h, blk, buf);
} else {
blk = get_free_dqblk(h);
if (blk < 0) {
freedqbuf(buf);
*err = blk;
return 0;
}
memset(buf, 0, QT_BLKSIZE);
info->dqi_free_entry = blk;
mark_quotafile_info_dirty(h);
}
/* Block will be full? */
if (ext2fs_le16_to_cpu(dh->dqdh_entries) + 1 >=
qtree_dqstr_in_blk(info))
remove_free_dqentry(h, buf, blk);
dh->dqdh_entries =
ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) + 1);
/* Find free structure in block */
ddquot = buf + sizeof(struct qt_disk_dqdbheader);
for (i = 0;
i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
i++)
ddquot += info->dqi_entry_size;
if (i == qtree_dqstr_in_blk(info))
log_err("find_free_dqentry(): Data block full unexpectedly.");
write_blk(h, blk, buf);
dquot->dq_dqb.u.v2_mdqb.dqb_off =
(blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
i * info->dqi_entry_size;
freedqbuf(buf);
return blk;
}
/* Insert reference to structure into the trie */
static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
unsigned int * treeblk, int depth)
{
dqbuf_t buf;
int newson = 0, newact = 0;
__le32 *ref;
unsigned int newblk;
int ret = 0;
log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
buf = getdqbuf();
if (!buf)
return -ENOMEM;
if (!*treeblk) {
ret = get_free_dqblk(h);
if (ret < 0)
goto out_buf;
*treeblk = ret;
memset(buf, 0, QT_BLKSIZE);
newact = 1;
} else {
read_blk(h, *treeblk, buf);
}
ref = (__le32 *) buf;
newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
if (!newblk)
newson = 1;
if (depth == QT_TREEDEPTH - 1) {
if (newblk)
log_err("Inserting already present quota entry "
"(block %u).",
ref[get_index(dquot->dq_id, depth)]);
newblk = find_free_dqentry(h, dquot, &ret);
} else {
ret = do_insert_tree(h, dquot, &newblk, depth + 1);
}
if (newson && ret >= 0) {
ref[get_index(dquot->dq_id, depth)] =
ext2fs_cpu_to_le32(newblk);
write_blk(h, *treeblk, buf);
} else if (newact && ret < 0) {
put_free_dqblk(h, buf, *treeblk);
}
out_buf:
freedqbuf(buf);
return ret;
}
/* Wrapper for inserting quota structure into tree */
static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
{
unsigned int tmp = QT_TREEOFF;
if (do_insert_tree(h, dquot, &tmp, 0) < 0)
log_err("Cannot write quota (id %u): %s",
(unsigned int) dquot->dq_id, strerror(errno));
}
/* Write dquot to file */
void qtree_write_dquot(struct dquot *dquot)
{
errcode_t retval;
unsigned int ret;
char *ddquot;
struct quota_handle *h = dquot->dq_h;
struct qtree_mem_dqinfo *info =
&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u",
dquot->dq_dqb.u.v2_mdqb.dqb_off,
info->dqi_entry_size);
retval = ext2fs_get_mem(info->dqi_entry_size, &ddquot);
if (retval) {
errno = ENOMEM;
log_err("Quota write failed (id %u): %s",
(unsigned int)dquot->dq_id, strerror(errno));
return;
}
memset(ddquot, 0, info->dqi_entry_size);
if (!dquot->dq_dqb.u.v2_mdqb.dqb_off)
dq_insert_tree(dquot->dq_h, dquot);
info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u",
dquot->dq_dqb.u.v2_mdqb.dqb_off,
info->dqi_entry_size);
ret = h->e2fs_write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot,
info->dqi_entry_size);
if (ret != info->dqi_entry_size) {
if (ret > 0)
errno = ENOSPC;
log_err("Quota write failed (id %u): %s",
(unsigned int)dquot->dq_id, strerror(errno));
}
ext2fs_free_mem(&ddquot);
}
/* Free dquot entry in data block */
static void free_dqentry(struct quota_handle *h, struct dquot *dquot,
unsigned int blk)
{
struct qt_disk_dqdbheader *dh;
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
dqbuf_t buf = getdqbuf();
if (!buf)
return;
if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
log_err("Quota structure has offset to other block (%u) "
"than it should (%u).", blk,
(unsigned int) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
QT_BLKSIZE_BITS));
read_blk(h, blk, buf);
dh = (struct qt_disk_dqdbheader *)buf;
dh->dqdh_entries =
ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) - 1);
if (!ext2fs_le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
remove_free_dqentry(h, buf, blk);
put_free_dqblk(h, buf, blk);
} else {
memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off &
((1 << QT_BLKSIZE_BITS) - 1)),
0, info->dqi_entry_size);
/* First free entry? */
if (ext2fs_le16_to_cpu(dh->dqdh_entries) ==
qtree_dqstr_in_blk(info) - 1)
/* This will also write data block */
insert_free_dqentry(h, buf, blk);
else
write_blk(h, blk, buf);
}
dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
freedqbuf(buf);
}
/* Remove reference to dquot from tree */
static void remove_tree(struct quota_handle *h, struct dquot *dquot,
unsigned int * blk, int depth)
{
dqbuf_t buf = getdqbuf();
unsigned int newblk;
__le32 *ref = (__le32 *) buf;
if (!buf)
return;
read_blk(h, *blk, buf);
newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
if (depth == QT_TREEDEPTH - 1) {
free_dqentry(h, dquot, newblk);
newblk = 0;
} else {
remove_tree(h, dquot, &newblk, depth + 1);
}
if (!newblk) {
int i;
ref[get_index(dquot->dq_id, depth)] = ext2fs_cpu_to_le32(0);
/* Block got empty? */
for (i = 0; i < QT_BLKSIZE && !buf[i]; i++);
/* Don't put the root block into the free block list */
if (i == QT_BLKSIZE && *blk != QT_TREEOFF) {
put_free_dqblk(h, buf, *blk);
*blk = 0;
} else {
write_blk(h, *blk, buf);
}
}
freedqbuf(buf);
}
/* Delete dquot from tree */
void qtree_delete_dquot(struct dquot *dquot)
{
unsigned int tmp = QT_TREEOFF;
if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
return;
remove_tree(dquot->dq_h, dquot, &tmp, 0);
}
/* Find entry in block */
static ext2_loff_t find_block_dqentry(struct quota_handle *h,
struct dquot *dquot, unsigned int blk)
{
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
dqbuf_t buf = getdqbuf();
int i;
char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
if (!buf)
return -ENOMEM;
read_blk(h, blk, buf);
for (i = 0;
i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
i++)
ddquot += info->dqi_entry_size;
if (i == qtree_dqstr_in_blk(info))
log_err("Quota for id %u referenced but not present.",
dquot->dq_id);
freedqbuf(buf);
return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
i * info->dqi_entry_size;
}
/* Find entry for given id in the tree */
static ext2_loff_t find_tree_dqentry(struct quota_handle *h,
struct dquot *dquot,
unsigned int blk, int depth)
{
dqbuf_t buf = getdqbuf();
ext2_loff_t ret = 0;
__le32 *ref = (__le32 *) buf;
if (!buf)
return -ENOMEM;
read_blk(h, blk, buf);
ret = 0;
blk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
if (!blk) /* No reference? */
goto out_buf;
if (depth < QT_TREEDEPTH - 1)
ret = find_tree_dqentry(h, dquot, blk, depth + 1);
else
ret = find_block_dqentry(h, dquot, blk);
out_buf:
freedqbuf(buf);
return ret;
}
/* Find entry for given id in the tree - wrapper function */
static inline ext2_loff_t find_dqentry(struct quota_handle *h,
struct dquot *dquot)
{
return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
}
/*
* Read dquot from disk.
*/
struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
{
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
ext2_loff_t offset;
unsigned int ret;
char *ddquot;
struct dquot *dquot = get_empty_dquot();
if (!dquot)
return NULL;
if (ext2fs_get_mem(info->dqi_entry_size, &ddquot)) {
ext2fs_free_mem(&dquot);
return NULL;
}
dquot->dq_id = id;
dquot->dq_h = h;
dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
offset = find_dqentry(h, dquot);
if (offset > 0) {
dquot->dq_dqb.u.v2_mdqb.dqb_off = offset;
ret = h->e2fs_read(&h->qh_qf, offset, ddquot,
info->dqi_entry_size);
if (ret != info->dqi_entry_size) {
if (ret > 0)
errno = EIO;
log_err("Cannot read quota structure for id %u: %s",
dquot->dq_id, strerror(errno));
}
info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
}
ext2fs_free_mem(&ddquot);
return dquot;
}
static int check_reference(struct quota_handle *h, unsigned int blk)
{
if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) {
log_err("Illegal reference (%u >= %u) in %s quota file",
blk, h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
quota_type2name(h->qh_type));
return -1;
}
return 0;
}
/*
* Scan all dquots in file and call callback on each
*/
#define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
#define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap,
int (*process_dquot) (struct dquot *, void *),
void *data)
{
struct qtree_mem_dqinfo *info =
&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
dqbuf_t buf = getdqbuf();
struct qt_disk_dqdbheader *dh;
char *ddata;
int entries, i;
if (!buf)
return -1;
set_bit(bitmap, blk);
read_blk(dquot->dq_h, blk, buf);
dh = (struct qt_disk_dqdbheader *)buf;
ddata = buf + sizeof(struct qt_disk_dqdbheader);
entries = ext2fs_le16_to_cpu(dh->dqdh_entries);
for (i = 0; i < qtree_dqstr_in_blk(info);
i++, ddata += info->dqi_entry_size)
if (!qtree_entry_unused(info, ddata)) {
dquot->dq_dqb.u.v2_mdqb.dqb_off =
(blk << QT_BLKSIZE_BITS) +
sizeof(struct qt_disk_dqdbheader) +
i * info->dqi_entry_size;
info->dqi_ops->disk2mem_dqblk(dquot, ddata);
if (process_dquot(dquot, data) < 0)
break;
}
freedqbuf(buf);
return entries;
}
static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
char *bitmap,
int (*process_dquot) (struct dquot *, void *),
void *data)
{
int entries = 0, ret, i;
dqbuf_t buf = getdqbuf();
__le32 *ref = (__le32 *) buf;
if (!buf)
return -1;
read_blk(dquot->dq_h, blk, buf);
if (depth == QT_TREEDEPTH - 1) {
for (i = 0; i < QT_BLKSIZE >> 2; i++) {
blk = ext2fs_le32_to_cpu(ref[i]);
if (check_reference(dquot->dq_h, blk)) {
entries = -1;
goto errout;
}
if (blk && !get_bit(bitmap, blk)) {
ret = report_block(dquot, blk, bitmap,
process_dquot, data);
if (ret < 0) {
entries = ret;
goto errout;
}
entries += ret;
}
}
} else {
for (i = 0; i < QT_BLKSIZE >> 2; i++) {
blk = ext2fs_le32_to_cpu(ref[i]);
if (blk) {
if (check_reference(dquot->dq_h, blk)) {
entries = -1;
goto errout;
}
ret = report_tree(dquot, blk, depth + 1,
bitmap, process_dquot,
data);
if (ret < 0) {
entries = ret;
goto errout;
}
entries += ret;
}
}
}
errout:
freedqbuf(buf);
return entries;
}
static unsigned int find_set_bits(char *bmp, int blocks)
{
unsigned int used = 0;
int i;
for (i = 0; i < blocks; i++)
if (get_bit(bmp, i))
used++;
return used;
}
int qtree_scan_dquots(struct quota_handle *h,
int (*process_dquot) (struct dquot *, void *),
void *data)
{
int ret;
char *bitmap;
struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
struct dquot *dquot = get_empty_dquot();
if (!dquot)
return -1;
dquot->dq_h = h;
if (ext2fs_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap)) {
ext2fs_free_mem(&dquot);
return -1;
}
ret = report_tree(dquot, QT_TREEOFF, 0, bitmap, process_dquot, data);
if (ret < 0)
goto errout;
v2info->dqi_used_entries = ret;
v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
ret = 0;
errout:
ext2fs_free_mem(&bitmap);
ext2fs_free_mem(&dquot);
return ret;
}

View File

@@ -0,0 +1,389 @@
/*
* Implementation of new quotafile format
*
* Jan Kara <jack@suse.cz> - sponsored by SuSE CR
*/
#include "config.h"
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "quotaio_v2.h"
#include "dqblk_v2.h"
#include "quotaio.h"
#include "quotaio_tree.h"
static int v2_check_file(struct quota_handle *h, int type, int fmt);
static int v2_init_io(struct quota_handle *h);
static int v2_new_io(struct quota_handle *h);
static int v2_write_info(struct quota_handle *h);
static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id);
static int v2_commit_dquot(struct dquot *dquot);
static int v2_scan_dquots(struct quota_handle *h,
int (*process_dquot) (struct dquot *dquot,
void *data),
void *data);
static int v2_report(struct quota_handle *h, int verbose);
struct quotafile_ops quotafile_ops_2 = {
.check_file = v2_check_file,
.init_io = v2_init_io,
.new_io = v2_new_io,
.write_info = v2_write_info,
.read_dquot = v2_read_dquot,
.commit_dquot = v2_commit_dquot,
.scan_dquots = v2_scan_dquots,
.report = v2_report,
};
/*
* Copy dquot from disk to memory
*/
static void v2r0_disk2memdqblk(struct dquot *dquot, void *dp)
{
struct util_dqblk *m = &dquot->dq_dqb;
struct v2r0_disk_dqblk *d = dp, empty;
dquot->dq_id = ext2fs_le32_to_cpu(d->dqb_id);
m->dqb_ihardlimit = ext2fs_le32_to_cpu(d->dqb_ihardlimit);
m->dqb_isoftlimit = ext2fs_le32_to_cpu(d->dqb_isoftlimit);
m->dqb_bhardlimit = ext2fs_le32_to_cpu(d->dqb_bhardlimit);
m->dqb_bsoftlimit = ext2fs_le32_to_cpu(d->dqb_bsoftlimit);
m->dqb_curinodes = ext2fs_le32_to_cpu(d->dqb_curinodes);
m->dqb_curspace = ext2fs_le64_to_cpu(d->dqb_curspace);
m->dqb_itime = ext2fs_le64_to_cpu(d->dqb_itime);
m->dqb_btime = ext2fs_le64_to_cpu(d->dqb_btime);
memset(&empty, 0, sizeof(struct v2r0_disk_dqblk));
empty.dqb_itime = ext2fs_cpu_to_le64(1);
if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk)))
m->dqb_itime = 0;
}
/*
* Copy dquot from memory to disk
*/
static void v2r0_mem2diskdqblk(void *dp, struct dquot *dquot)
{
struct util_dqblk *m = &dquot->dq_dqb;
struct v2r0_disk_dqblk *d = dp;
d->dqb_ihardlimit = ext2fs_cpu_to_le32(m->dqb_ihardlimit);
d->dqb_isoftlimit = ext2fs_cpu_to_le32(m->dqb_isoftlimit);
d->dqb_bhardlimit = ext2fs_cpu_to_le32(m->dqb_bhardlimit);
d->dqb_bsoftlimit = ext2fs_cpu_to_le32(m->dqb_bsoftlimit);
d->dqb_curinodes = ext2fs_cpu_to_le32(m->dqb_curinodes);
d->dqb_curspace = ext2fs_cpu_to_le64(m->dqb_curspace);
d->dqb_itime = ext2fs_cpu_to_le64(m->dqb_itime);
d->dqb_btime = ext2fs_cpu_to_le64(m->dqb_btime);
d->dqb_id = ext2fs_cpu_to_le32(dquot->dq_id);
if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp))
d->dqb_itime = ext2fs_cpu_to_le64(1);
}
static int v2r0_is_id(void *dp, struct dquot *dquot)
{
struct v2r0_disk_dqblk *d = dp;
struct qtree_mem_dqinfo *info =
&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
if (qtree_entry_unused(info, dp))
return 0;
return ext2fs_le32_to_cpu(d->dqb_id) == dquot->dq_id;
}
static struct qtree_fmt_operations v2r0_fmt_ops = {
.mem2disk_dqblk = v2r0_mem2diskdqblk,
.disk2mem_dqblk = v2r0_disk2memdqblk,
.is_id = v2r0_is_id,
};
/*
* Copy dquot from disk to memory
*/
static void v2r1_disk2memdqblk(struct dquot *dquot, void *dp)
{
struct util_dqblk *m = &dquot->dq_dqb;
struct v2r1_disk_dqblk *d = dp, empty;
dquot->dq_id = ext2fs_le32_to_cpu(d->dqb_id);
m->dqb_ihardlimit = ext2fs_le64_to_cpu(d->dqb_ihardlimit);
m->dqb_isoftlimit = ext2fs_le64_to_cpu(d->dqb_isoftlimit);
m->dqb_bhardlimit = ext2fs_le64_to_cpu(d->dqb_bhardlimit);
m->dqb_bsoftlimit = ext2fs_le64_to_cpu(d->dqb_bsoftlimit);
m->dqb_curinodes = ext2fs_le64_to_cpu(d->dqb_curinodes);
m->dqb_curspace = ext2fs_le64_to_cpu(d->dqb_curspace);
m->dqb_itime = ext2fs_le64_to_cpu(d->dqb_itime);
m->dqb_btime = ext2fs_le64_to_cpu(d->dqb_btime);
memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
empty.dqb_itime = ext2fs_cpu_to_le64(1);
if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
m->dqb_itime = 0;
}
/*
* Copy dquot from memory to disk
*/
static void v2r1_mem2diskdqblk(void *dp, struct dquot *dquot)
{
struct util_dqblk *m = &dquot->dq_dqb;
struct v2r1_disk_dqblk *d = dp;
d->dqb_ihardlimit = ext2fs_cpu_to_le64(m->dqb_ihardlimit);
d->dqb_isoftlimit = ext2fs_cpu_to_le64(m->dqb_isoftlimit);
d->dqb_bhardlimit = ext2fs_cpu_to_le64(m->dqb_bhardlimit);
d->dqb_bsoftlimit = ext2fs_cpu_to_le64(m->dqb_bsoftlimit);
d->dqb_curinodes = ext2fs_cpu_to_le64(m->dqb_curinodes);
d->dqb_curspace = ext2fs_cpu_to_le64(m->dqb_curspace);
d->dqb_itime = ext2fs_cpu_to_le64(m->dqb_itime);
d->dqb_btime = ext2fs_cpu_to_le64(m->dqb_btime);
d->dqb_id = ext2fs_cpu_to_le32(dquot->dq_id);
if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp))
d->dqb_itime = ext2fs_cpu_to_le64(1);
}
static int v2r1_is_id(void *dp, struct dquot *dquot)
{
struct v2r1_disk_dqblk *d = dp;
struct qtree_mem_dqinfo *info =
&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
if (qtree_entry_unused(info, dp))
return 0;
return ext2fs_le32_to_cpu(d->dqb_id) == dquot->dq_id;
}
static struct qtree_fmt_operations v2r1_fmt_ops = {
.mem2disk_dqblk = v2r1_mem2diskdqblk,
.disk2mem_dqblk = v2r1_disk2memdqblk,
.is_id = v2r1_is_id,
};
/*
* Copy dqinfo from disk to memory
*/
static inline void v2_disk2memdqinfo(struct util_dqinfo *m,
struct v2_disk_dqinfo *d)
{
m->dqi_bgrace = ext2fs_le32_to_cpu(d->dqi_bgrace);
m->dqi_igrace = ext2fs_le32_to_cpu(d->dqi_igrace);
m->u.v2_mdqi.dqi_flags = ext2fs_le32_to_cpu(d->dqi_flags) & V2_DQF_MASK;
m->u.v2_mdqi.dqi_qtree.dqi_blocks = ext2fs_le32_to_cpu(d->dqi_blocks);
m->u.v2_mdqi.dqi_qtree.dqi_free_blk =
ext2fs_le32_to_cpu(d->dqi_free_blk);
m->u.v2_mdqi.dqi_qtree.dqi_free_entry =
ext2fs_le32_to_cpu(d->dqi_free_entry);
}
/*
* Copy dqinfo from memory to disk
*/
static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d,
struct util_dqinfo *m)
{
d->dqi_bgrace = ext2fs_cpu_to_le32(m->dqi_bgrace);
d->dqi_igrace = ext2fs_cpu_to_le32(m->dqi_igrace);
d->dqi_flags = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK);
d->dqi_blocks = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks);
d->dqi_free_blk =
ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk);
d->dqi_free_entry =
ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry);
}
static int v2_read_header(struct quota_handle *h, struct v2_disk_dqheader *dqh)
{
if (h->e2fs_read(&h->qh_qf, 0, dqh, sizeof(struct v2_disk_dqheader)) !=
sizeof(struct v2_disk_dqheader))
return 0;
return 1;
}
/*
* Check whether given quota file is in our format
*/
static int v2_check_file(struct quota_handle *h, int type, int fmt)
{
struct v2_disk_dqheader dqh;
int file_magics[] = INITQMAGICS;
int be_magic;
if (fmt != QFMT_VFS_V1)
return 0;
if (!v2_read_header(h, &dqh))
return 0;
be_magic = ext2fs_be32_to_cpu((__force __be32)dqh.dqh_magic);
if (be_magic == file_magics[type]) {
log_err("Your quota file is stored in wrong endianness");
return 0;
}
if (V2_VERSION_R0 != ext2fs_le32_to_cpu(dqh.dqh_version) &&
V2_VERSION_R1 != ext2fs_le32_to_cpu(dqh.dqh_version))
return 0;
return 1;
}
/*
* Open quotafile
*/
static int v2_init_io(struct quota_handle *h)
{
struct v2_disk_dqheader dqh;
struct v2_disk_dqinfo ddqinfo;
struct v2_mem_dqinfo *info;
__u64 filesize;
int version;
if (!v2_read_header(h, &dqh))
return -1;
version = ext2fs_le32_to_cpu(dqh.dqh_version);
if (version == V2_VERSION_R0) {
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
sizeof(struct v2r0_disk_dqblk);
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r0_fmt_ops;
} else {
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
sizeof(struct v2r1_disk_dqblk);
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
}
/* Read information about quotafile */
if (h->e2fs_read(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
sizeof(ddqinfo)) != sizeof(ddqinfo))
return -1;
v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
/* Check to make sure quota file info is sane */
info = &h->qh_info.u.v2_mdqi;
if (ext2fs_file_get_lsize(h->qh_qf.e2_file, &filesize))
return -1;
if ((filesize > (1U << 31)) ||
(info->dqi_qtree.dqi_blocks >
(filesize + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS)) {
log_err("Quota inode %u corrupted: file size %llu; "
"dqi_blocks %u", h->qh_qf.ino,
(unsigned long long) filesize,
info->dqi_qtree.dqi_blocks);
return -1;
}
if (info->dqi_qtree.dqi_free_blk >= info->dqi_qtree.dqi_blocks) {
log_err("Quota inode %u corrupted: free_blk %u; dqi_blocks %u",
h->qh_qf.ino, info->dqi_qtree.dqi_free_blk,
info->dqi_qtree.dqi_blocks);
return -1;
}
if (info->dqi_qtree.dqi_free_entry >= info->dqi_qtree.dqi_blocks) {
log_err("Quota inode %u corrupted: free_entry %u; "
"dqi_blocks %u", h->qh_qf.ino,
info->dqi_qtree.dqi_free_entry,
info->dqi_qtree.dqi_blocks);
return -1;
}
return 0;
}
/*
* Initialize new quotafile
*/
static int v2_new_io(struct quota_handle *h)
{
int file_magics[] = INITQMAGICS;
struct v2_disk_dqheader ddqheader;
struct v2_disk_dqinfo ddqinfo;
if (h->qh_fmt != QFMT_VFS_V1)
return -1;
/* Write basic quota header */
ddqheader.dqh_magic = ext2fs_cpu_to_le32(file_magics[h->qh_type]);
ddqheader.dqh_version = ext2fs_cpu_to_le32(V2_VERSION_R1);
if (h->e2fs_write(&h->qh_qf, 0, &ddqheader, sizeof(ddqheader)) !=
sizeof(ddqheader))
return -1;
/* Write information about quotafile */
h->qh_info.dqi_bgrace = MAX_DQ_TIME;
h->qh_info.dqi_igrace = MAX_IQ_TIME;
h->qh_info.u.v2_mdqi.dqi_flags = 0;
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = QT_TREEOFF + 1;
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk = 0;
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = 0;
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
sizeof(struct v2r1_disk_dqblk);
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
if (h->e2fs_write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
sizeof(ddqinfo)) !=
sizeof(ddqinfo))
return -1;
return 0;
}
/*
* Write information (grace times to file)
*/
static int v2_write_info(struct quota_handle *h)
{
struct v2_disk_dqinfo ddqinfo;
v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
if (h->e2fs_write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)) !=
sizeof(ddqinfo))
return -1;
return 0;
}
/*
* Read dquot from disk
*/
static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id)
{
return qtree_read_dquot(h, id);
}
/*
* Commit changes of dquot to disk - it might also mean deleting it when quota
* became fake one and user has no blocks.
* User can process use 'errno' to detect errstr.
*/
static int v2_commit_dquot(struct dquot *dquot)
{
struct util_dqblk *b = &dquot->dq_dqb;
if (!b->dqb_curspace && !b->dqb_curinodes && !b->dqb_bsoftlimit &&
!b->dqb_isoftlimit && !b->dqb_bhardlimit && !b->dqb_ihardlimit)
qtree_delete_dquot(dquot);
else
qtree_write_dquot(dquot);
return 0;
}
static int v2_scan_dquots(struct quota_handle *h,
int (*process_dquot) (struct dquot *, void *),
void *data)
{
return qtree_scan_dquots(h, process_dquot, data);
}
/* Report information about quotafile.
* TODO: Not used right now, but we should be able to use this when we add
* support to debugfs to read quota files.
*/
static int v2_report(struct quota_handle *h EXT2FS_ATTR((unused)),
int verbose EXT2FS_ATTR((unused)))
{
log_err("Not Implemented.");
return -1;
}