pmt: e2fsprogs: cleanup
This commit is contained in:
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,706 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Binary file not shown.
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
Binary file not shown.
@@ -1,287 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,317 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Binary file not shown.
@@ -1,412 +0,0 @@
|
||||
/** 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 = "afile_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 = "afile_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;
|
||||
}
|
||||
Binary file not shown.
@@ -1,687 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Binary file not shown.
@@ -1,389 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user