mirror of
https://gitee.com/openharmony/third_party_f2fs-tools
synced 2024-11-23 01:59:54 +00:00
fsck.f2fs: support quota
This patch let fsck to check and fix quota file contents. Signed-off-by: Hyojun Kim <hyojun@google.com> Signed-off-by: Jaegeuk Kim <jaegeuk@google.com>
This commit is contained in:
parent
23a872f9ba
commit
6c6bb35c89
@ -5,7 +5,8 @@ AM_CFLAGS = -Wall
|
||||
sbin_PROGRAMS = fsck.f2fs
|
||||
fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c defrag.c f2fs.h fsck.h $(top_srcdir)/include/f2fs_fs.h \
|
||||
resize.c \
|
||||
node.c segment.c dir.c sload.c xattr.c
|
||||
node.c segment.c dir.c sload.c xattr.c \
|
||||
dict.c mkquota.c quotaio.c quotaio_tree.c quotaio_v2.c
|
||||
fsck_f2fs_LDADD = ${libselinux_LIBS} ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
|
||||
|
||||
install-data-hook:
|
||||
|
30
fsck/common.h
Normal file
30
fsck/common.h
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
*
|
||||
* Various things common for all utilities
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __QUOTA_COMMON_H__
|
||||
#define __QUOTA_COMMON_H__
|
||||
|
||||
#undef DEBUG_QUOTA
|
||||
|
||||
#ifndef __attribute__
|
||||
# if !defined __GNUC__ || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
|
||||
# define __attribute__(x)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define log_err(format, arg ...) \
|
||||
fprintf(stderr, "[ERROR] %s:%d:%s:: " format "\n", \
|
||||
__FILE__, __LINE__, __func__, ## arg)
|
||||
|
||||
#ifdef DEBUG_QUOTA
|
||||
# define log_debug(format, arg ...) \
|
||||
fprintf(stderr, "[DEBUG] %s:%d:%s:: " format "\n", \
|
||||
__FILE__, __LINE__, __func__, ## arg)
|
||||
#else
|
||||
# define log_debug(...)
|
||||
#endif
|
||||
|
||||
#endif /* __QUOTA_COMMON_H__ */
|
1501
fsck/dict.c
Normal file
1501
fsck/dict.c
Normal file
File diff suppressed because it is too large
Load Diff
144
fsck/dict.h
Normal file
144
fsck/dict.h
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Dictionary Abstract Data Type
|
||||
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
|
||||
*
|
||||
* Free Software License:
|
||||
*
|
||||
* All rights are reserved by the author, with the following exceptions:
|
||||
* Permission is granted to freely reproduce and distribute this software,
|
||||
* possibly in exchange for a fee, provided that this copyright notice appears
|
||||
* intact. Permission is also granted to adapt this software to produce
|
||||
* derivative works, as long as the modified versions carry this copyright
|
||||
* notice and additional notices stating that the work has been modified.
|
||||
* This source code may be translated into executable form and incorporated
|
||||
* into proprietary software; there is no requirement for such software to
|
||||
* contain a copyright notice related to this source.
|
||||
*
|
||||
* $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $
|
||||
* $Name: kazlib_1_20 $
|
||||
*/
|
||||
|
||||
#ifndef DICT_H
|
||||
#define DICT_H
|
||||
|
||||
#include <limits.h>
|
||||
#ifdef KAZLIB_SIDEEFFECT_DEBUG
|
||||
#include "sfx.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Blurb for inclusion into C++ translation units
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef unsigned long dictcount_t;
|
||||
#define DICTCOUNT_T_MAX ULONG_MAX
|
||||
|
||||
/*
|
||||
* The dictionary is implemented as a red-black tree
|
||||
*/
|
||||
|
||||
typedef enum { dnode_red, dnode_black } dnode_color_t;
|
||||
|
||||
typedef struct dnode_t {
|
||||
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
||||
struct dnode_t *dict_left;
|
||||
struct dnode_t *dict_right;
|
||||
struct dnode_t *dict_parent;
|
||||
dnode_color_t dict_color;
|
||||
const void *dict_key;
|
||||
void *dict_data;
|
||||
#else
|
||||
int dict_dummy;
|
||||
#endif
|
||||
} dnode_t;
|
||||
|
||||
typedef int (*dict_comp_t)(const void *, const void *);
|
||||
typedef dnode_t *(*dnode_alloc_t)(void *);
|
||||
typedef void (*dnode_free_t)(dnode_t *, void *);
|
||||
|
||||
typedef struct dict_t {
|
||||
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
||||
dnode_t dict_nilnode;
|
||||
dictcount_t dict_nodecount;
|
||||
dictcount_t dict_maxcount;
|
||||
dict_comp_t dict_compare;
|
||||
dnode_alloc_t dict_allocnode;
|
||||
dnode_free_t dict_freenode;
|
||||
void *dict_context;
|
||||
int dict_dupes;
|
||||
#else
|
||||
int dict_dummmy;
|
||||
#endif
|
||||
} dict_t;
|
||||
|
||||
typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
|
||||
|
||||
typedef struct dict_load_t {
|
||||
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
||||
dict_t *dict_dictptr;
|
||||
dnode_t dict_nilnode;
|
||||
#else
|
||||
int dict_dummmy;
|
||||
#endif
|
||||
} dict_load_t;
|
||||
|
||||
extern dict_t *dict_create(dictcount_t, dict_comp_t);
|
||||
extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
|
||||
extern void dict_destroy(dict_t *);
|
||||
extern void dict_free_nodes(dict_t *);
|
||||
extern void dict_free(dict_t *);
|
||||
extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
|
||||
extern void dict_init_like(dict_t *, const dict_t *);
|
||||
extern int dict_verify(dict_t *);
|
||||
extern int dict_similar(const dict_t *, const dict_t *);
|
||||
extern dnode_t *dict_lookup(dict_t *, const void *);
|
||||
extern dnode_t *dict_lower_bound(dict_t *, const void *);
|
||||
extern dnode_t *dict_upper_bound(dict_t *, const void *);
|
||||
extern void dict_insert(dict_t *, dnode_t *, const void *);
|
||||
extern dnode_t *dict_delete(dict_t *, dnode_t *);
|
||||
extern int dict_alloc_insert(dict_t *, const void *, void *);
|
||||
extern void dict_delete_free(dict_t *, dnode_t *);
|
||||
extern dnode_t *dict_first(dict_t *);
|
||||
extern dnode_t *dict_last(dict_t *);
|
||||
extern dnode_t *dict_next(dict_t *, dnode_t *);
|
||||
extern dnode_t *dict_prev(dict_t *, dnode_t *);
|
||||
extern dictcount_t dict_count(dict_t *);
|
||||
extern int dict_isempty(dict_t *);
|
||||
extern int dict_isfull(dict_t *);
|
||||
extern int dict_contains(dict_t *, dnode_t *);
|
||||
extern void dict_allow_dupes(dict_t *);
|
||||
extern int dnode_is_in_a_dict(dnode_t *);
|
||||
extern dnode_t *dnode_create(void *);
|
||||
extern dnode_t *dnode_init(dnode_t *, void *);
|
||||
extern void dnode_destroy(dnode_t *);
|
||||
extern void *dnode_get(dnode_t *);
|
||||
extern const void *dnode_getkey(dnode_t *);
|
||||
extern void dnode_put(dnode_t *, void *);
|
||||
extern void dict_process(dict_t *, void *, dnode_process_t);
|
||||
extern void dict_load_begin(dict_load_t *, dict_t *);
|
||||
extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
|
||||
extern void dict_load_end(dict_load_t *);
|
||||
extern void dict_merge(dict_t *, dict_t *);
|
||||
|
||||
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
||||
#ifdef KAZLIB_SIDEEFFECT_DEBUG
|
||||
#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
|
||||
#else
|
||||
#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
|
||||
#endif
|
||||
#define dict_count(D) ((D)->dict_nodecount)
|
||||
#define dict_isempty(D) ((D)->dict_nodecount == 0)
|
||||
#define dnode_get(N) ((N)->dict_data)
|
||||
#define dnode_getkey(N) ((N)->dict_key)
|
||||
#define dnode_put(N, X) ((N)->dict_data = (X))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -314,7 +314,7 @@ static void make_empty_dir(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
|
||||
nid_t pino = le32_to_cpu(inode->i.i_pino);
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
block_t blkaddr;
|
||||
block_t blkaddr = NULL_ADDR;
|
||||
int ret;
|
||||
|
||||
get_node_info(sbi, ino, &ni);
|
||||
@ -354,7 +354,7 @@ static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode,
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
char *data_blk;
|
||||
block_t blkaddr;
|
||||
block_t blkaddr = NULL_ADDR;
|
||||
int ret;
|
||||
|
||||
get_node_info(sbi, ino, &ni);
|
||||
@ -553,7 +553,7 @@ int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
|
||||
struct f2fs_node *parent, *child;
|
||||
struct node_info ni;
|
||||
struct f2fs_summary sum;
|
||||
block_t blkaddr;
|
||||
block_t blkaddr = NULL_ADDR;
|
||||
int ret;
|
||||
|
||||
/* Find if there is a */
|
||||
|
31
fsck/dqblk_v2.h
Normal file
31
fsck/dqblk_v2.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Header file for disk format of new quotafile format
|
||||
*
|
||||
* Jan Kara <jack@suse.cz> - sponsored by SuSE CR
|
||||
*/
|
||||
|
||||
#ifndef __QUOTA_DQBLK_V2_H__
|
||||
#define __QUOTA_DQBLK_V2_H__
|
||||
|
||||
#include "quotaio_tree.h"
|
||||
|
||||
/* Structure for format specific information */
|
||||
struct v2_mem_dqinfo {
|
||||
struct qtree_mem_dqinfo dqi_qtree;
|
||||
unsigned int dqi_flags; /* Flags set in quotafile */
|
||||
unsigned int dqi_used_entries; /* Number of entries in file -
|
||||
updated by scan_dquots */
|
||||
unsigned int dqi_data_blocks; /* Number of data blocks in file -
|
||||
updated by scan_dquots */
|
||||
};
|
||||
|
||||
struct v2_mem_dqblk {
|
||||
long long dqb_off; /* Offset of dquot in file */
|
||||
};
|
||||
|
||||
struct quotafile_ops; /* Will be defined later in quotaio.h */
|
||||
|
||||
/* Operations above this format */
|
||||
extern struct quotafile_ops quotafile_ops_2;
|
||||
|
||||
#endif /* __QUOTA_DQBLK_V2_H__ */
|
105
fsck/fsck.c
105
fsck/fsck.c
@ -9,12 +9,12 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include "fsck.h"
|
||||
#include "quotaio.h"
|
||||
|
||||
char *tree_mark;
|
||||
uint32_t tree_mark_size = 256;
|
||||
|
||||
static inline int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk,
|
||||
int type)
|
||||
int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk, int type)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
struct seg_entry *se;
|
||||
@ -50,6 +50,13 @@ static inline int f2fs_test_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk)
|
||||
return f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap);
|
||||
}
|
||||
|
||||
int f2fs_set_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
|
||||
return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap);
|
||||
}
|
||||
|
||||
static int add_into_hard_link_list(struct f2fs_sb_info *sbi,
|
||||
u32 nid, u32 link_cnt)
|
||||
{
|
||||
@ -500,7 +507,9 @@ int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
|
||||
goto err;
|
||||
|
||||
if (ntype == TYPE_INODE) {
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, &ni);
|
||||
quota_add_inode_usage(fsck->qctx, nid, &node_blk->i);
|
||||
} else {
|
||||
switch (ntype) {
|
||||
case TYPE_DIRECT_NODE:
|
||||
@ -622,7 +631,8 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
|
||||
if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) {
|
||||
f2fs_set_main_bitmap(sbi, ni->blk_addr,
|
||||
CURSEG_WARM_NODE);
|
||||
if (i_links > 1 && ftype != F2FS_FT_ORPHAN) {
|
||||
if (i_links > 1 && ftype != F2FS_FT_ORPHAN &&
|
||||
!is_qf_ino(F2FS_RAW_SUPER(sbi), nid)) {
|
||||
/* First time. Create new hard link node */
|
||||
add_into_hard_link_list(sbi, nid, i_links);
|
||||
fsck->chk.multi_hard_link_files++;
|
||||
@ -807,6 +817,11 @@ skip_blkcnt_fix:
|
||||
le32_to_cpu(node_blk->footer.ino),
|
||||
en, (u32)i_blocks);
|
||||
|
||||
if (is_qf_ino(F2FS_RAW_SUPER(sbi), nid))
|
||||
DBG(1, "Quota Inode: 0x%x [%s] i_blocks: %u\n\n",
|
||||
le32_to_cpu(node_blk->footer.ino),
|
||||
en, (u32)i_blocks);
|
||||
|
||||
if (ftype == F2FS_FT_DIR) {
|
||||
DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n",
|
||||
le32_to_cpu(node_blk->footer.ino), en,
|
||||
@ -1558,6 +1573,82 @@ int fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsck_chk_quota_node(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
enum quota_type qtype;
|
||||
int ret = 0;
|
||||
u32 blk_cnt = 0;
|
||||
|
||||
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
|
||||
if (sb->qf_ino[qtype] == 0)
|
||||
continue;
|
||||
nid_t ino = QUOTA_INO(sb, qtype);
|
||||
struct node_info ni;
|
||||
|
||||
DBG(1, "[%3d] ino [0x%x]\n", qtype, ino);
|
||||
blk_cnt = 1;
|
||||
|
||||
if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) {
|
||||
get_node_info(sbi, ino, &ni);
|
||||
if (!IS_VALID_NID(sbi, ino) ||
|
||||
!IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = fsck_chk_node_blk(sbi, NULL, ino,
|
||||
F2FS_FT_REG_FILE, TYPE_INODE, &blk_cnt, NULL);
|
||||
if (ret)
|
||||
ASSERT_MSG("[0x%x] wrong orphan inode", ino);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fsck_chk_quota_files(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
enum quota_type qtype;
|
||||
f2fs_ino_t ino;
|
||||
int ret = 0;
|
||||
int needs_writeout;
|
||||
|
||||
/* Return if quota feature is disabled */
|
||||
if (!fsck->qctx)
|
||||
return 0;
|
||||
|
||||
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
|
||||
ino = sb->qf_ino[qtype];
|
||||
if (!ino)
|
||||
continue;
|
||||
|
||||
DBG(1, "Checking Quota file ([%3d] ino [0x%x])\n", qtype, ino);
|
||||
needs_writeout = 0;
|
||||
ret = quota_compare_and_update(sbi, qtype, &needs_writeout);
|
||||
if (ret == 0 && needs_writeout == 0) {
|
||||
DBG(1, "OK\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Something is wrong */
|
||||
if (c.fix_on) {
|
||||
DBG(0, "Fixing Quota file ([%3d] ino [0x%x])\n",
|
||||
qtype, ino);
|
||||
f2fs_filesize_update(sbi, ino, 0);
|
||||
ret = quota_write_inode(sbi, qtype);
|
||||
if (!ret) {
|
||||
c.bug_on = 1;
|
||||
DBG(1, "OK\n");
|
||||
} else {
|
||||
ASSERT_MSG("Unable to write quota file");
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG("Quota file is missing or invalid"
|
||||
" quota file content found.");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fsck_chk_meta(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
@ -1618,6 +1709,10 @@ int fsck_chk_meta(struct f2fs_sb_info *sbi)
|
||||
if (fsck_chk_orphan_node(sbi))
|
||||
return -EINVAL;
|
||||
|
||||
/* 5. check quota inode simply */
|
||||
if (fsck_chk_quota_node(sbi))
|
||||
return -EINVAL;
|
||||
|
||||
if (fsck->nat_valid_inode_cnt != le32_to_cpu(cp->valid_inode_count)) {
|
||||
ASSERT_MSG("valid inode does not match: nat_valid_inode_cnt %u,"
|
||||
" valid_inode_count %u",
|
||||
@ -2042,6 +2137,10 @@ int fsck_verify(struct f2fs_sb_info *sbi)
|
||||
void fsck_free(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
|
||||
if (fsck->qctx)
|
||||
quota_release_context(&fsck->qctx);
|
||||
|
||||
if (fsck->main_area_bitmap)
|
||||
free(fsck->main_area_bitmap);
|
||||
|
||||
|
10
fsck/fsck.h
10
fsck/fsck.h
@ -13,6 +13,8 @@
|
||||
|
||||
#include "f2fs.h"
|
||||
|
||||
struct quota_ctx;
|
||||
|
||||
#define FSCK_UNMATCHED_EXTENT 0x00000001
|
||||
|
||||
enum {
|
||||
@ -85,6 +87,8 @@ struct f2fs_fsck {
|
||||
u32 dentry_depth;
|
||||
struct f2fs_nat_entry *entries;
|
||||
u32 nat_valid_inode_cnt;
|
||||
|
||||
struct quota_ctx *qctx;
|
||||
};
|
||||
|
||||
#define BLOCK_SZ 4096
|
||||
@ -118,6 +122,8 @@ enum seg_type {
|
||||
struct selabel_handle;
|
||||
|
||||
extern int fsck_chk_orphan_node(struct f2fs_sb_info *);
|
||||
extern int fsck_chk_quota_node(struct f2fs_sb_info *);
|
||||
extern int fsck_chk_quota_files(struct f2fs_sb_info *);
|
||||
extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
|
||||
enum FILE_TYPE, enum NODE_TYPE, u32 *,
|
||||
struct child_info *);
|
||||
@ -154,6 +160,8 @@ extern void nullify_nat_entry(struct f2fs_sb_info *, u32);
|
||||
extern void rewrite_sit_area_bitmap(struct f2fs_sb_info *);
|
||||
extern void build_nat_area_bitmap(struct f2fs_sb_info *);
|
||||
extern void build_sit_area_bitmap(struct f2fs_sb_info *);
|
||||
extern int f2fs_set_main_bitmap(struct f2fs_sb_info *, u32, int);
|
||||
extern int f2fs_set_sit_bitmap(struct f2fs_sb_info *, u32);
|
||||
extern void fsck_init(struct f2fs_sb_info *);
|
||||
extern int fsck_verify(struct f2fs_sb_info *);
|
||||
extern void fsck_free(struct f2fs_sb_info *);
|
||||
@ -210,6 +218,8 @@ int f2fs_resize(struct f2fs_sb_info *);
|
||||
/* sload.c */
|
||||
int f2fs_sload(struct f2fs_sb_info *, const char *, const char *,
|
||||
const char *, struct selabel_handle *);
|
||||
|
||||
/* segment.c */
|
||||
void reserve_new_block(struct f2fs_sb_info *, block_t *,
|
||||
struct f2fs_summary *, int);
|
||||
void new_data_block(struct f2fs_sb_info *, void *,
|
||||
|
18
fsck/main.c
18
fsck/main.c
@ -18,6 +18,7 @@
|
||||
#include "fsck.h"
|
||||
#include <libgen.h>
|
||||
#include <ctype.h>
|
||||
#include "quotaio.h"
|
||||
|
||||
struct f2fs_fsck gfsck;
|
||||
|
||||
@ -407,6 +408,7 @@ static void do_fsck(struct f2fs_sb_info *sbi)
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||
u32 flag = le32_to_cpu(ckpt->ckpt_flags);
|
||||
u32 blk_cnt;
|
||||
errcode_t ret;
|
||||
|
||||
fsck_init(sbi);
|
||||
|
||||
@ -429,8 +431,7 @@ static void do_fsck(struct f2fs_sb_info *sbi)
|
||||
c.fix_on = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
} else { /*
|
||||
* we can hit this in 3 situations:
|
||||
* 1. fsck -f, fix_on has already been set to 1 when
|
||||
* parsing options;
|
||||
@ -443,12 +444,23 @@ static void do_fsck(struct f2fs_sb_info *sbi)
|
||||
c.fix_on = 1;
|
||||
}
|
||||
|
||||
fsck_chk_orphan_node(sbi);
|
||||
fsck_chk_quota_node(sbi);
|
||||
|
||||
/* Traverse all block recursively from root inode */
|
||||
blk_cnt = 1;
|
||||
|
||||
if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) {
|
||||
ret = quota_init_context(sbi);
|
||||
if (ret) {
|
||||
ASSERT_MSG("quota_init_context failure: %d", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fsck_chk_orphan_node(sbi);
|
||||
fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
|
||||
F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
|
||||
fsck_chk_quota_files(sbi);
|
||||
|
||||
fsck_verify(sbi);
|
||||
fsck_free(sbi);
|
||||
}
|
||||
|
403
fsck/mkquota.c
Normal file
403
fsck/mkquota.c
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
* mkquota.c --- create quota files for a filesystem
|
||||
*
|
||||
* Aditya Kali <adityakali@google.com>
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.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_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
|
||||
#define print_dquot(...)
|
||||
#endif
|
||||
|
||||
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(struct f2fs_sb_info *sbi, enum quota_type qtype)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
quota_ctx_t qctx = fsck->qctx;
|
||||
struct quota_handle *h = NULL;
|
||||
int retval = 0;
|
||||
dict_t *dict;
|
||||
|
||||
if ((!qctx) || (!sb->qf_ino[qtype]))
|
||||
return 0;
|
||||
|
||||
retval = quota_get_mem(sizeof(struct quota_handle), &h);
|
||||
if (retval) {
|
||||
log_debug("Unable to allocate quota handle");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dict = qctx->quota_dict[qtype];
|
||||
if (dict) {
|
||||
retval = quota_file_create(sbi, h, qtype);
|
||||
if (retval) {
|
||||
log_debug("Cannot initialize io on quotafile");
|
||||
} else {
|
||||
write_dquots(dict, h);
|
||||
quota_file_close(sbi, h, 1);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (h)
|
||||
quota_free_mem(&h);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* Helper functions for computing quota in memory. */
|
||||
/******************************************************************/
|
||||
|
||||
static int dict_uint_cmp(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 qid_t get_qid(struct f2fs_inode *inode, enum quota_type qtype)
|
||||
{
|
||||
switch (qtype) {
|
||||
case USRQUOTA:
|
||||
return inode->i_uid;
|
||||
case GRPQUOTA:
|
||||
return inode->i_gid;
|
||||
case PRJQUOTA:
|
||||
return inode->i_projid;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void quota_dnode_free(dnode_t *node, void *UNUSED(context))
|
||||
{
|
||||
void *ptr = node ? dnode_get(node) : 0;
|
||||
|
||||
quota_free_mem(&ptr);
|
||||
free(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the quota tracking data structures.
|
||||
*/
|
||||
errcode_t quota_init_context(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
errcode_t err;
|
||||
dict_t *dict;
|
||||
quota_ctx_t ctx;
|
||||
enum quota_type qtype;
|
||||
|
||||
err = quota_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));
|
||||
dict_init(&ctx->linked_inode_dict, DICTCOUNT_T_MAX, dict_uint_cmp);
|
||||
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
|
||||
ctx->quota_file[qtype] = NULL;
|
||||
if (!sb->qf_ino[qtype])
|
||||
continue;
|
||||
err = quota_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->sbi = sbi;
|
||||
fsck->qctx = ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void quota_release_context(quota_ctx_t *qctx)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
dict_free_nodes(&ctx->linked_inode_dict);
|
||||
*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 (quota_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 f2fs_inode *inode, qsize_t space)
|
||||
{
|
||||
struct dquot *dq;
|
||||
dict_t *dict;
|
||||
enum quota_type qtype;
|
||||
|
||||
if (!qctx)
|
||||
return;
|
||||
|
||||
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
|
||||
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 f2fs_inode *inode, qsize_t space)
|
||||
{
|
||||
struct dquot *dq;
|
||||
dict_t *dict;
|
||||
enum quota_type qtype;
|
||||
|
||||
if (!qctx)
|
||||
return;
|
||||
|
||||
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
|
||||
dict = qctx->quota_dict[qtype];
|
||||
if (dict) {
|
||||
dq = get_dq(dict, get_qid(inode, qtype));
|
||||
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 f2fs_inode *inode, int adjust)
|
||||
{
|
||||
struct dquot *dq;
|
||||
dict_t *dict; enum quota_type qtype;
|
||||
|
||||
if (!qctx)
|
||||
return;
|
||||
|
||||
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
|
||||
dict = qctx->quota_dict[qtype];
|
||||
if (dict) {
|
||||
dq = get_dq(dict, get_qid(inode, qtype));
|
||||
dq->dq_dqb.dqb_curinodes += adjust;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from fsck to count quota.
|
||||
*/
|
||||
void quota_add_inode_usage(quota_ctx_t qctx, f2fs_ino_t ino,
|
||||
struct f2fs_inode* inode)
|
||||
{
|
||||
if (qctx) {
|
||||
/* Handle hard linked inodes */
|
||||
if (inode->i_links > 1) {
|
||||
if (dict_lookup(&qctx->linked_inode_dict,
|
||||
UINT_TO_VOIDPTR(ino))) {
|
||||
return;
|
||||
}
|
||||
dict_alloc_insert(&qctx->linked_inode_dict,
|
||||
UINT_TO_VOIDPTR(ino), NULL);
|
||||
}
|
||||
|
||||
qsize_t space = (inode->i_blocks - 1) * BLOCK_SZ;
|
||||
quota_data_add(qctx, inode, space);
|
||||
quota_data_inodes(qctx, inode, +1);
|
||||
}
|
||||
}
|
||||
|
||||
struct scan_dquots_data {
|
||||
dict_t *quota_dict;
|
||||
int update_limits; /* update limits from disk */
|
||||
int update_usage;
|
||||
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);
|
||||
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 (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;
|
||||
log_debug("[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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(struct f2fs_sb_info *sbi,
|
||||
enum quota_type qtype, int *usage_inconsistent)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
quota_ctx_t qctx = fsck->qctx;
|
||||
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(sbi, &qh, qtype, 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.usage_is_inconsistent = 0;
|
||||
err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
|
||||
if (err) {
|
||||
log_debug("Error scanning dquots");
|
||||
goto out;
|
||||
}
|
||||
|
||||
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) {
|
||||
log_debug("[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:
|
||||
return err;
|
||||
}
|
||||
|
15
fsck/mount.c
15
fsck/mount.c
@ -297,6 +297,9 @@ void print_sb_state(struct f2fs_super_block *sb)
|
||||
if (f & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR)) {
|
||||
MSG(0, "%s", " flexible inline xattr");
|
||||
}
|
||||
if (f & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) {
|
||||
MSG(0, "%s", " quota ino");
|
||||
}
|
||||
MSG(0, "\n");
|
||||
MSG(0, "Info: superblock encrypt level = %d, salt = ",
|
||||
sb->encryption_level);
|
||||
@ -739,7 +742,7 @@ static int f2fs_init_nid_bitmap(struct f2fs_sb_info *sbi)
|
||||
nid_t nid;
|
||||
int i;
|
||||
|
||||
if (!(c.func == SLOAD))
|
||||
if (!(c.func == SLOAD || c.func == FSCK))
|
||||
return 0;
|
||||
|
||||
nm_i->nid_bitmap = (char *)calloc(nid_bitmap_size, 1);
|
||||
@ -2159,10 +2162,14 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi)
|
||||
if (c.auto_fix || c.preen_mode) {
|
||||
u32 flag = get_cp(ckpt_flags);
|
||||
|
||||
if (flag & CP_FSCK_FLAG)
|
||||
if (flag & CP_FSCK_FLAG ||
|
||||
(exist_qf_ino(sb) && (!(flag & CP_UMOUNT_FLAG) ||
|
||||
flag & CP_ERROR_FLAG))) {
|
||||
c.fix_on = 1;
|
||||
else if (!c.preen_mode)
|
||||
} else if (!c.preen_mode) {
|
||||
print_cp_state(flag);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
c.bug_on = 0;
|
||||
@ -2224,7 +2231,7 @@ void f2fs_do_umount(struct f2fs_sb_info *sbi)
|
||||
unsigned int i;
|
||||
|
||||
/* free nm_info */
|
||||
if (c.func == SLOAD)
|
||||
if (c.func == SLOAD || c.func == FSCK)
|
||||
free(nm_i->nid_bitmap);
|
||||
free(nm_i->nat_bitmap);
|
||||
free(sbi->nm_info);
|
||||
|
@ -63,7 +63,7 @@ block_t new_node_block(struct f2fs_sb_info *sbi,
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
block_t blkaddr;
|
||||
block_t blkaddr = NULL_ADDR;
|
||||
int type;
|
||||
|
||||
f2fs_inode = dn->inode_blk;
|
||||
|
221
fsck/quotaio.c
Normal file
221
fsck/quotaio.c
Normal file
@ -0,0 +1,221 @@
|
||||
/** quotaio.c
|
||||
*
|
||||
* Generic IO operations on quotafiles
|
||||
* Jan Kara <jack@suse.cz> - sponsored by SuSE CR
|
||||
* Aditya Kali <adityakali@google.com> - Ported to e2fsprogs
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
|
||||
#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",
|
||||
};
|
||||
|
||||
/* 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];
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Functions to read/write quota file. */
|
||||
static unsigned int quota_write_nomount(struct quota_file *qf,
|
||||
long offset,
|
||||
void *buf, unsigned int size)
|
||||
{
|
||||
unsigned int written;
|
||||
|
||||
written = f2fs_write(qf->sbi, qf->ino, buf, size, offset);
|
||||
if (qf->filesize < offset + written)
|
||||
qf->filesize = offset + written;
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static unsigned int quota_read_nomount(struct quota_file *qf, long offset,
|
||||
void *buf, unsigned int size)
|
||||
{
|
||||
return f2fs_read(qf->sbi, qf->ino, buf, size, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect quota format and initialize quota IO
|
||||
*/
|
||||
errcode_t quota_file_open(struct f2fs_sb_info *sbi, struct quota_handle *h,
|
||||
enum quota_type qtype, int flags)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
quota_ctx_t qctx = fsck->qctx;
|
||||
f2fs_ino_t qf_ino;
|
||||
errcode_t err = 0;
|
||||
int allocated_handle = 0;
|
||||
|
||||
if (qtype >= MAXQUOTAS)
|
||||
return EINVAL;
|
||||
|
||||
qf_ino = sb->qf_ino[qtype];
|
||||
|
||||
if (!h) {
|
||||
if (qctx->quota_file[qtype]) {
|
||||
h = qctx->quota_file[qtype];
|
||||
(void) quota_file_close(sbi, h, 0);
|
||||
}
|
||||
err = quota_get_mem(sizeof(struct quota_handle), &h);
|
||||
if (err) {
|
||||
log_err("Unable to allocate quota handle");
|
||||
return err;
|
||||
}
|
||||
allocated_handle = 1;
|
||||
}
|
||||
|
||||
h->qh_qf.sbi = sbi;
|
||||
h->qh_qf.ino = qf_ino;
|
||||
h->write = quota_write_nomount;
|
||||
h->read = quota_read_nomount;
|
||||
h->qh_file_flags = flags;
|
||||
h->qh_io_flags = 0;
|
||||
h->qh_type = qtype;
|
||||
h->qh_fmt = QFMT_VFS_V1;
|
||||
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) == 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;
|
||||
errout:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new quotafile of specified format on given filesystem
|
||||
*/
|
||||
errcode_t quota_file_create(struct f2fs_sb_info *sbi, struct quota_handle *h,
|
||||
enum quota_type qtype)
|
||||
{
|
||||
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
|
||||
f2fs_ino_t qf_inum = sb->qf_ino[qtype];
|
||||
errcode_t err = 0;
|
||||
|
||||
h->qh_qf.sbi = sbi;
|
||||
h->qh_qf.ino = qf_inum;
|
||||
h->write = quota_write_nomount;
|
||||
h->read = quota_read_nomount;
|
||||
|
||||
log_debug("Creating quota ino=%u, type=%d", qf_inum, qtype);
|
||||
h->qh_io_flags = 0;
|
||||
h->qh_type = qtype;
|
||||
h->qh_fmt = QFMT_VFS_V1;
|
||||
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;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close quotafile and release handle
|
||||
*/
|
||||
errcode_t quota_file_close(struct f2fs_sb_info *sbi, struct quota_handle *h,
|
||||
int update_filesize)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
quota_ctx_t qctx = fsck->qctx;
|
||||
|
||||
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 (update_filesize) {
|
||||
f2fs_filesize_update(sbi, h->qh_qf.ino, h->qh_qf.filesize);
|
||||
}
|
||||
if (qctx->quota_file[h->qh_type] == h)
|
||||
quota_free_mem(&qctx->quota_file[h->qh_type]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create empty quota structure
|
||||
*/
|
||||
struct dquot *get_empty_dquot(void)
|
||||
{
|
||||
struct dquot *dquot;
|
||||
|
||||
if (quota_get_memzero(sizeof(struct dquot), &dquot)) {
|
||||
log_err("Failed to allocate dquot");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dquot->dq_id = -1;
|
||||
return dquot;
|
||||
}
|
255
fsck/quotaio.h
Normal file
255
fsck/quotaio.h
Normal file
@ -0,0 +1,255 @@
|
||||
/** quotaio.h
|
||||
*
|
||||
* Interface to the quota library.
|
||||
*
|
||||
* The quota library provides interface for creating and updating the quota
|
||||
* files and the ext4 superblock fields. It supports the new VFS_V1 quota
|
||||
* format. The quota library also provides support for keeping track of quotas
|
||||
* in memory.
|
||||
*
|
||||
* Aditya Kali <adityakali@google.com>
|
||||
* Header of IO operations for quota utilities
|
||||
*
|
||||
* Jan Kara <jack@suse.cz>
|
||||
*
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
|
||||
#ifndef GUARD_QUOTAIO_H
|
||||
#define GUARD_QUOTAIO_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "dict.h"
|
||||
#include "f2fs_fs.h"
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
#include "fsck.h"
|
||||
|
||||
#include "dqblk_v2.h"
|
||||
|
||||
typedef int64_t qsize_t; /* Type in which we store size limitations */
|
||||
typedef int32_t f2fs_ino_t;
|
||||
typedef int errcode_t;
|
||||
|
||||
enum quota_type {
|
||||
USRQUOTA = 0,
|
||||
GRPQUOTA = 1,
|
||||
PRJQUOTA = 2,
|
||||
MAXQUOTAS = 3,
|
||||
};
|
||||
|
||||
#if MAXQUOTAS > 32
|
||||
#error "cannot have more than 32 quota types to fit in qtype_bits"
|
||||
#endif
|
||||
|
||||
|
||||
#define QUOTA_USR_BIT (1 << USRQUOTA)
|
||||
#define QUOTA_GRP_BIT (1 << GRPQUOTA)
|
||||
#define QUOTA_PRJ_BIT (1 << PRJQUOTA)
|
||||
#define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT | QUOTA_PRJ_BIT)
|
||||
|
||||
typedef struct quota_ctx *quota_ctx_t;
|
||||
|
||||
struct quota_ctx {
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct dict_t *quota_dict[MAXQUOTAS];
|
||||
struct quota_handle *quota_file[MAXQUOTAS];
|
||||
struct dict_t linked_inode_dict;
|
||||
};
|
||||
|
||||
/*
|
||||
* Definitions of magics and versions of current quota files
|
||||
*/
|
||||
#define INITQMAGICS {\
|
||||
0xd9c01f11, /* USRQUOTA */\
|
||||
0xd9c01927, /* GRPQUOTA */\
|
||||
0xd9c03f14 /* PRJQUOTA */\
|
||||
}
|
||||
|
||||
/* Size of blocks in which are counted size limits in generic utility parts */
|
||||
#define QUOTABLOCK_BITS 10
|
||||
#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
|
||||
#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
|
||||
|
||||
/* Quota format type IDs */
|
||||
#define QFMT_VFS_OLD 1
|
||||
#define QFMT_VFS_V0 2
|
||||
#define QFMT_VFS_V1 4
|
||||
|
||||
/*
|
||||
* The following constants define the default amount of time given a user
|
||||
* before the soft limits are treated as hard limits (usually resulting
|
||||
* in an allocation failure). The timer is started when the user crosses
|
||||
* their soft limit, it is reset when they go below their soft limit.
|
||||
*/
|
||||
#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
|
||||
#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
|
||||
|
||||
#define IOFL_INFODIRTY 0x01 /* Did info change? */
|
||||
|
||||
struct quotafile_ops;
|
||||
|
||||
/* Generic information about quotafile */
|
||||
struct util_dqinfo {
|
||||
time_t dqi_bgrace; /* Block grace time for given quotafile */
|
||||
time_t dqi_igrace; /* Inode grace time for given quotafile */
|
||||
union {
|
||||
struct v2_mem_dqinfo v2_mdqi;
|
||||
} u; /* Format specific info about quotafile */
|
||||
};
|
||||
|
||||
struct quota_file {
|
||||
struct f2fs_sb_info *sbi;
|
||||
f2fs_ino_t ino;
|
||||
int64_t filesize;
|
||||
};
|
||||
|
||||
/* Structure for one opened quota file */
|
||||
struct quota_handle {
|
||||
enum quota_type qh_type; /* Type of quotafile */
|
||||
int qh_fmt; /* Quotafile format */
|
||||
int qh_file_flags;
|
||||
int qh_io_flags; /* IO flags for file */
|
||||
struct quota_file qh_qf;
|
||||
unsigned int (*read)(struct quota_file *qf, long offset,
|
||||
void *buf, unsigned int size);
|
||||
unsigned int (*write)(struct quota_file *qf, long offset,
|
||||
void *buf, unsigned int size);
|
||||
struct quotafile_ops *qh_ops; /* Operations on quotafile */
|
||||
struct util_dqinfo qh_info; /* Generic quotafile info */
|
||||
};
|
||||
|
||||
/* Utility quota block */
|
||||
struct util_dqblk {
|
||||
qsize_t dqb_ihardlimit;
|
||||
qsize_t dqb_isoftlimit;
|
||||
qsize_t dqb_curinodes;
|
||||
qsize_t dqb_bhardlimit;
|
||||
qsize_t dqb_bsoftlimit;
|
||||
qsize_t dqb_curspace;
|
||||
time_t dqb_btime;
|
||||
time_t dqb_itime;
|
||||
union {
|
||||
struct v2_mem_dqblk v2_mdqb;
|
||||
} u; /* Format specific dquot information */
|
||||
};
|
||||
|
||||
/* Structure for one loaded quota */
|
||||
struct dquot {
|
||||
struct dquot *dq_next; /* Pointer to next dquot in the list */
|
||||
qid_t dq_id; /* ID dquot belongs to */
|
||||
int dq_flags; /* Some flags for utils */
|
||||
struct quota_handle *dq_h; /* Handle of quotafile for this dquot */
|
||||
struct util_dqblk dq_dqb; /* Parsed data of dquot */
|
||||
};
|
||||
|
||||
#define DQF_SEEN 0x0001
|
||||
|
||||
/* Structure of quotafile operations */
|
||||
struct quotafile_ops {
|
||||
/* Check whether quotafile is in our format */
|
||||
int (*check_file) (struct quota_handle *h, int type);
|
||||
/* Open quotafile */
|
||||
int (*init_io) (struct quota_handle *h);
|
||||
/* Create new quotafile */
|
||||
int (*new_io) (struct quota_handle *h);
|
||||
/* Write all changes and close quotafile */
|
||||
int (*end_io) (struct quota_handle *h);
|
||||
/* Write info about quotafile */
|
||||
int (*write_info) (struct quota_handle *h);
|
||||
/* Read dquot into memory */
|
||||
struct dquot *(*read_dquot) (struct quota_handle *h, qid_t id);
|
||||
/* Write given dquot to disk */
|
||||
int (*commit_dquot) (struct dquot *dquot);
|
||||
/* Scan quotafile and call callback on every structure */
|
||||
int (*scan_dquots) (struct quota_handle *h,
|
||||
int (*process_dquot) (struct dquot *dquot,
|
||||
void *data),
|
||||
void *data);
|
||||
/* Function to print format specific file information */
|
||||
int (*report) (struct quota_handle *h, int verbose);
|
||||
};
|
||||
|
||||
#ifdef __CHECKER__
|
||||
# ifndef __bitwise
|
||||
# define __bitwise __attribute__((bitwise))
|
||||
# endif
|
||||
#define __force __attribute__((force))
|
||||
#else
|
||||
# ifndef __bitwise
|
||||
# define __bitwise
|
||||
# endif
|
||||
#define __force
|
||||
#endif
|
||||
|
||||
#define be32_to_cpu(n) ntohl(n)
|
||||
|
||||
/* Open existing quotafile of given type (and verify its format) on given
|
||||
* filesystem. */
|
||||
errcode_t quota_file_open(struct f2fs_sb_info *sbi, struct quota_handle *h,
|
||||
enum quota_type qtype, int flags);
|
||||
|
||||
/* Create new quotafile of specified format on given filesystem */
|
||||
errcode_t quota_file_create(struct f2fs_sb_info *sbi, struct quota_handle *h,
|
||||
enum quota_type qtype);
|
||||
|
||||
/* Close quotafile */
|
||||
errcode_t quota_file_close(struct f2fs_sb_info *sbi, struct quota_handle *h,
|
||||
int update_filesize);
|
||||
|
||||
/* Get empty quota structure */
|
||||
struct dquot *get_empty_dquot(void);
|
||||
const char *quota_type2name(enum quota_type qtype);
|
||||
void update_grace_times(struct dquot *q);
|
||||
|
||||
/* In mkquota.c */
|
||||
errcode_t quota_init_context(struct f2fs_sb_info *sbi);
|
||||
void quota_data_inodes(quota_ctx_t qctx, struct f2fs_inode *inode, int adjust);
|
||||
void quota_data_add(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space);
|
||||
void quota_data_sub(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space);
|
||||
errcode_t quota_write_inode(struct f2fs_sb_info *sbi, enum quota_type qtype);
|
||||
void quota_add_inode_usage(quota_ctx_t qctx, f2fs_ino_t ino,
|
||||
struct f2fs_inode* inode);
|
||||
void quota_release_context(quota_ctx_t *qctx);
|
||||
errcode_t quota_compare_and_update(struct f2fs_sb_info *sbi,
|
||||
enum quota_type qtype, int *usage_inconsistent);
|
||||
|
||||
static inline errcode_t quota_get_mem(unsigned long size, void *ptr)
|
||||
{
|
||||
void *pp;
|
||||
|
||||
pp = malloc(size);
|
||||
if (!pp)
|
||||
return -1;
|
||||
memcpy(ptr, &pp, sizeof (pp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline errcode_t quota_get_memzero(unsigned long size, void *ptr)
|
||||
{
|
||||
void *pp;
|
||||
|
||||
pp = malloc(size);
|
||||
if (!pp)
|
||||
return -1;
|
||||
memset(pp, 0, size);
|
||||
memcpy(ptr, &pp, sizeof(pp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline errcode_t quota_free_mem(void *ptr)
|
||||
{
|
||||
void *p;
|
||||
|
||||
memcpy(&p, ptr, sizeof(p));
|
||||
free(p);
|
||||
p = 0;
|
||||
memcpy(ptr, &p, sizeof(p));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* GUARD_QUOTAIO_H */
|
679
fsck/quotaio_tree.c
Normal file
679
fsck/quotaio_tree.c
Normal file
@ -0,0 +1,679 @@
|
||||
/*
|
||||
* Implementation of new quotafile format
|
||||
*
|
||||
* Jan Kara <jack@suse.cz> - sponsored by SuSE CR
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
|
||||
#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) quota_free_mem(&buf)
|
||||
|
||||
static inline dqbuf_t getdqbuf(void)
|
||||
{
|
||||
dqbuf_t buf;
|
||||
if (quota_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->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->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 = 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 = cpu_to_le32(info->dqi_free_blk);
|
||||
dh->dqdh_prev_free = cpu_to_le32(0);
|
||||
dh->dqdh_entries = 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 = le32_to_cpu(dh->dqdh_next_free), prevblk =
|
||||
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 = 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 = cpu_to_le32(info->dqi_free_entry);
|
||||
dh->dqdh_prev_free = 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 =
|
||||
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 (le16_to_cpu(dh->dqdh_entries) + 1 >=
|
||||
qtree_dqstr_in_blk(info))
|
||||
remove_free_dqentry(h, buf, blk);
|
||||
|
||||
dh->dqdh_entries =
|
||||
cpu_to_le16(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 = 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)] =
|
||||
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 = quota_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->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));
|
||||
}
|
||||
quota_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 =
|
||||
cpu_to_le16(le16_to_cpu(dh->dqdh_entries) - 1);
|
||||
|
||||
if (!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 (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 = 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)] = 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 long 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 long find_tree_dqentry(struct quota_handle *h,
|
||||
struct dquot *dquot,
|
||||
unsigned int blk, int depth)
|
||||
{
|
||||
dqbuf_t buf = getdqbuf();
|
||||
long ret = 0;
|
||||
__le32 *ref = (__le32 *) buf;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
read_blk(h, blk, buf);
|
||||
ret = 0;
|
||||
blk = 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 long 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;
|
||||
long offset;
|
||||
unsigned int ret;
|
||||
char *ddquot;
|
||||
struct dquot *dquot = get_empty_dquot();
|
||||
|
||||
if (!dquot)
|
||||
return NULL;
|
||||
if (quota_get_mem(info->dqi_entry_size, &ddquot)) {
|
||||
quota_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->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);
|
||||
}
|
||||
quota_free_mem(&ddquot);
|
||||
return dquot;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 0;
|
||||
|
||||
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 = 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 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. "
|
||||
"Quota file is probably corrupted.\n"
|
||||
"Please run fsck (8) to fix it.",
|
||||
blk,
|
||||
h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
|
||||
quota_type2name(h->qh_type));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return 0 for successful run */
|
||||
static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
|
||||
char *bitmap, int *entries,
|
||||
int (*process_dquot) (struct dquot *, void *),
|
||||
void *data)
|
||||
{
|
||||
int i;
|
||||
dqbuf_t buf = getdqbuf();
|
||||
__le32 *ref = (__le32 *) buf;
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
read_blk(dquot->dq_h, blk, buf);
|
||||
for (i = 0; i < QT_BLKSIZE >> 2; i++) {
|
||||
blk = le32_to_cpu(ref[i]);
|
||||
if (blk == 0)
|
||||
continue;
|
||||
|
||||
if (check_reference(dquot->dq_h, blk))
|
||||
break;
|
||||
|
||||
if (depth == QT_TREEDEPTH - 1) {
|
||||
if (!get_bit(bitmap, blk))
|
||||
*entries += report_block(dquot, blk, bitmap,
|
||||
process_dquot, data);
|
||||
} else {
|
||||
if (report_tree(dquot, blk, depth + 1, bitmap, entries,
|
||||
process_dquot, data))
|
||||
break;
|
||||
}
|
||||
}
|
||||
freedqbuf(buf);
|
||||
return (i < QT_BLKSIZE >> 2) ? -1 : 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
char *bitmap = NULL;
|
||||
int ret = -1;
|
||||
int entries = 0;
|
||||
|
||||
if (!dquot)
|
||||
return -1;
|
||||
|
||||
dquot->dq_h = h;
|
||||
if (quota_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap))
|
||||
goto out;
|
||||
if (report_tree(dquot, QT_TREEOFF, 0, bitmap, &entries, process_dquot,
|
||||
data))
|
||||
goto out;
|
||||
|
||||
v2info->dqi_used_entries = entries;
|
||||
v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (bitmap)
|
||||
quota_free_mem(&bitmap);
|
||||
if (dquot)
|
||||
quota_free_mem(&dquot);
|
||||
|
||||
return ret;
|
||||
}
|
66
fsck/quotaio_tree.h
Normal file
66
fsck/quotaio_tree.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Definitions of structures for vfsv0 quota format
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_QUOTA_TREE_H
|
||||
#define _LINUX_QUOTA_TREE_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <linux/types.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef __u32 qid_t; /* Type in which we store ids in memory */
|
||||
|
||||
#define QT_TREEOFF 1 /* Offset of tree in file in blocks */
|
||||
#define QT_TREEDEPTH 4 /* Depth of quota tree */
|
||||
#define QT_BLKSIZE_BITS 10
|
||||
#define QT_BLKSIZE (1 << QT_BLKSIZE_BITS) /* Size of block with quota
|
||||
* structures */
|
||||
|
||||
/*
|
||||
* Structure of header of block with quota structures. It is padded to 16 bytes
|
||||
* so there will be space for exactly 21 quota-entries in a block
|
||||
*/
|
||||
struct qt_disk_dqdbheader {
|
||||
__le32 dqdh_next_free; /* Number of next block with free
|
||||
* entry */
|
||||
__le32 dqdh_prev_free; /* Number of previous block with free
|
||||
* entry */
|
||||
__le16 dqdh_entries; /* Number of valid entries in block */
|
||||
__le16 dqdh_pad1;
|
||||
__le32 dqdh_pad2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct dquot;
|
||||
struct quota_handle;
|
||||
|
||||
/* Operations */
|
||||
struct qtree_fmt_operations {
|
||||
/* Convert given entry from in memory format to disk one */
|
||||
void (*mem2disk_dqblk)(void *disk, struct dquot *dquot);
|
||||
/* Convert given entry from disk format to in memory one */
|
||||
void (*disk2mem_dqblk)(struct dquot *dquot, void *disk);
|
||||
/* Is this structure for given id? */
|
||||
int (*is_id)(void *disk, struct dquot *dquot);
|
||||
};
|
||||
|
||||
/* Inmemory copy of version specific information */
|
||||
struct qtree_mem_dqinfo {
|
||||
unsigned int dqi_blocks; /* # of blocks in quota file */
|
||||
unsigned int dqi_free_blk; /* First block in list of free blocks */
|
||||
unsigned int dqi_free_entry; /* First block with free entry */
|
||||
unsigned int dqi_entry_size; /* Size of quota entry in quota file */
|
||||
struct qtree_fmt_operations *dqi_ops; /* Operations for entry
|
||||
* manipulation */
|
||||
};
|
||||
|
||||
void qtree_write_dquot(struct dquot *dquot);
|
||||
struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id);
|
||||
void qtree_delete_dquot(struct dquot *dquot);
|
||||
int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk);
|
||||
int qtree_scan_dquots(struct quota_handle *h,
|
||||
int (*process_dquot) (struct dquot *, void *), void *data);
|
||||
|
||||
int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info);
|
||||
|
||||
#endif /* _LINUX_QUOTAIO_TREE_H */
|
284
fsck/quotaio_v2.c
Normal file
284
fsck/quotaio_v2.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Implementation of new quotafile format
|
||||
*
|
||||
* Jan Kara <jack@suse.cz> - sponsored by SuSE CR
|
||||
* Hyojun Kim <hyojun@google.com> - Ported to f2fs-tools
|
||||
*/
|
||||
|
||||
#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_tree.h"
|
||||
|
||||
static int v2_check_file(struct quota_handle *h, int type);
|
||||
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 v2r1_disk2memdqblk(struct dquot *dquot, void *dp)
|
||||
{
|
||||
struct util_dqblk *m = &dquot->dq_dqb;
|
||||
struct v2r1_disk_dqblk *d = dp, empty;
|
||||
|
||||
dquot->dq_id = le32_to_cpu(d->dqb_id);
|
||||
m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
|
||||
m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
|
||||
m->dqb_bhardlimit = le64_to_cpu(d->dqb_bhardlimit);
|
||||
m->dqb_bsoftlimit = le64_to_cpu(d->dqb_bsoftlimit);
|
||||
m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
|
||||
m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
|
||||
m->dqb_itime = le64_to_cpu(d->dqb_itime);
|
||||
m->dqb_btime = le64_to_cpu(d->dqb_btime);
|
||||
|
||||
memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
|
||||
empty.dqb_itime = 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 = cpu_to_le64(m->dqb_ihardlimit);
|
||||
d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
|
||||
d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit);
|
||||
d->dqb_bsoftlimit = cpu_to_le64(m->dqb_bsoftlimit);
|
||||
d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
|
||||
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
|
||||
d->dqb_itime = cpu_to_le64(m->dqb_itime);
|
||||
d->dqb_btime = cpu_to_le64(m->dqb_btime);
|
||||
d->dqb_id = cpu_to_le32(dquot->dq_id);
|
||||
if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp))
|
||||
d->dqb_itime = 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 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 = le32_to_cpu(d->dqi_bgrace);
|
||||
m->dqi_igrace = le32_to_cpu(d->dqi_igrace);
|
||||
m->u.v2_mdqi.dqi_flags = le32_to_cpu(d->dqi_flags) & V2_DQF_MASK;
|
||||
m->u.v2_mdqi.dqi_qtree.dqi_blocks = le32_to_cpu(d->dqi_blocks);
|
||||
m->u.v2_mdqi.dqi_qtree.dqi_free_blk =
|
||||
le32_to_cpu(d->dqi_free_blk);
|
||||
m->u.v2_mdqi.dqi_qtree.dqi_free_entry =
|
||||
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 = cpu_to_le32(m->dqi_bgrace);
|
||||
d->dqi_igrace = cpu_to_le32(m->dqi_igrace);
|
||||
d->dqi_flags = cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK);
|
||||
d->dqi_blocks = cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks);
|
||||
d->dqi_free_blk =
|
||||
cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk);
|
||||
d->dqi_free_entry =
|
||||
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->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)
|
||||
{
|
||||
struct v2_disk_dqheader dqh;
|
||||
int file_magics[] = INITQMAGICS;
|
||||
int be_magic;
|
||||
|
||||
if (!v2_read_header(h, &dqh))
|
||||
return 0;
|
||||
|
||||
be_magic = be32_to_cpu((__force __be32)dqh.dqh_magic);
|
||||
if (be_magic == file_magics[type]) {
|
||||
log_err("Your quota file is stored in wrong endianity");
|
||||
return 0;
|
||||
}
|
||||
if (V2_VERSION != le32_to_cpu(dqh.dqh_version))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open quotafile
|
||||
*/
|
||||
static int v2_init_io(struct quota_handle *h)
|
||||
{
|
||||
struct v2_disk_dqinfo ddqinfo;
|
||||
|
||||
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->read(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
|
||||
sizeof(ddqinfo)) != sizeof(ddqinfo))
|
||||
return -1;
|
||||
v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
|
||||
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 = cpu_to_le32(file_magics[h->qh_type]);
|
||||
ddqheader.dqh_version = cpu_to_le32(V2_VERSION);
|
||||
if (h->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->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->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 *UNUSED(h), int UNUSED(verbose))
|
||||
{
|
||||
log_err("Not Implemented.");
|
||||
return -1;
|
||||
}
|
54
fsck/quotaio_v2.h
Normal file
54
fsck/quotaio_v2.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
*
|
||||
* Header file for disk format of new quotafile format
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GUARD_QUOTAIO_V2_H
|
||||
#define GUARD_QUOTAIO_V2_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "quotaio.h"
|
||||
|
||||
/* Offset of info header in file */
|
||||
#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader)
|
||||
/* Supported version of quota-tree format */
|
||||
#define V2_VERSION 1
|
||||
|
||||
struct v2_disk_dqheader {
|
||||
__le32 dqh_magic; /* Magic number identifying file */
|
||||
__le32 dqh_version; /* File version */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Flags for version specific files */
|
||||
#define V2_DQF_MASK 0x0000 /* Mask for all valid ondisk flags */
|
||||
|
||||
/* Header with type and version specific information */
|
||||
struct v2_disk_dqinfo {
|
||||
__le32 dqi_bgrace; /* Time before block soft limit becomes
|
||||
* hard limit */
|
||||
__le32 dqi_igrace; /* Time before inode soft limit becomes
|
||||
* hard limit */
|
||||
__le32 dqi_flags; /* Flags for quotafile (DQF_*) */
|
||||
__le32 dqi_blocks; /* Number of blocks in file */
|
||||
__le32 dqi_free_blk; /* Number of first free block in the list */
|
||||
__le32 dqi_free_entry; /* Number of block with at least one
|
||||
* free entry */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct v2r1_disk_dqblk {
|
||||
__le32 dqb_id; /* id this quota applies to */
|
||||
__le32 dqb_pad;
|
||||
__le64 dqb_ihardlimit; /* absolute limit on allocated inodes */
|
||||
__le64 dqb_isoftlimit; /* preferred inode limit */
|
||||
__le64 dqb_curinodes; /* current # allocated inodes */
|
||||
__le64 dqb_bhardlimit; /* absolute limit on disk space
|
||||
* (in QUOTABLOCK_SIZE) */
|
||||
__le64 dqb_bsoftlimit; /* preferred limit on disk space
|
||||
* (in QUOTABLOCK_SIZE) */
|
||||
__le64 dqb_curspace; /* current space occupied (in bytes) */
|
||||
__le64 dqb_btime; /* time limit for excessive disk use */
|
||||
__le64 dqb_itime; /* time limit for excessive inode use */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif
|
@ -27,9 +27,10 @@ static void write_inode(u64 blkaddr, struct f2fs_node *inode)
|
||||
void reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
|
||||
struct f2fs_summary *sum, int type)
|
||||
{
|
||||
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
|
||||
struct seg_entry *se;
|
||||
u64 blkaddr;
|
||||
u64 offset;
|
||||
u64 blkaddr, offset;
|
||||
u64 old_blkaddr = *to;
|
||||
|
||||
blkaddr = SM_I(sbi)->main_blkaddr;
|
||||
|
||||
@ -43,7 +44,16 @@ void reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
|
||||
se->type = type;
|
||||
se->valid_blocks++;
|
||||
f2fs_set_bit(offset, (char *)se->cur_valid_map);
|
||||
sbi->total_valid_block_count++;
|
||||
if (c.func == FSCK) {
|
||||
f2fs_set_main_bitmap(sbi, blkaddr, type);
|
||||
f2fs_set_sit_bitmap(sbi, blkaddr);
|
||||
}
|
||||
|
||||
if (old_blkaddr == NULL_ADDR) {
|
||||
sbi->total_valid_block_count++;
|
||||
if (c.func == FSCK)
|
||||
fsck->chk.valid_blk_cnt++;
|
||||
}
|
||||
se->dirty = 1;
|
||||
|
||||
/* read/write SSA */
|
||||
@ -56,6 +66,7 @@ void new_data_block(struct f2fs_sb_info *sbi, void *block,
|
||||
{
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
int blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
|
||||
|
||||
ASSERT(dn->node_blk);
|
||||
memset(block, 0, BLOCK_SZ);
|
||||
@ -64,7 +75,10 @@ void new_data_block(struct f2fs_sb_info *sbi, void *block,
|
||||
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
|
||||
reserve_new_block(sbi, &dn->data_blkaddr, &sum, type);
|
||||
|
||||
inc_inode_blocks(dn);
|
||||
if (blkaddr == NULL_ADDR)
|
||||
inc_inode_blocks(dn);
|
||||
else if (blkaddr == NEW_ADDR)
|
||||
dn->idirty = 1;
|
||||
set_data_blkaddr(dn);
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,15 @@
|
||||
#include <linux/blkzoned.h>
|
||||
#endif
|
||||
|
||||
#ifdef UNUSED
|
||||
#elif defined(__GNUC__)
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
|
||||
#elif defined(__LCLINT__)
|
||||
# define UNUSED(x) x
|
||||
#else
|
||||
# define UNUSED(x) x
|
||||
#endif
|
||||
|
||||
typedef u_int64_t u64;
|
||||
typedef u_int32_t u32;
|
||||
typedef u_int16_t u16;
|
||||
@ -1179,6 +1188,16 @@ static inline __le64 get_cp_crc(struct f2fs_checkpoint *cp)
|
||||
return cpu_to_le64(cp_ver);
|
||||
}
|
||||
|
||||
static inline int exist_qf_ino(struct f2fs_super_block *sb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < F2FS_MAX_QUOTAS; i++)
|
||||
if (sb->qf_ino[i])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_qf_ino(struct f2fs_super_block *sb, nid_t ino)
|
||||
{
|
||||
int i;
|
||||
|
@ -395,9 +395,13 @@ static int f2fs_prepare_super_block(void)
|
||||
quotatype_bits |= QUOTA_PRJ_BIT;
|
||||
}
|
||||
|
||||
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++)
|
||||
sb->qf_ino[qtype] =
|
||||
((1 << qtype) & quotatype_bits) ? next_ino++ : 0;
|
||||
for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) {
|
||||
if (!((1 << qtype) & quotatype_bits))
|
||||
continue;
|
||||
sb->qf_ino[qtype] = cpu_to_le32(next_ino++);
|
||||
MSG(0, "Info: add quota type = %u => %u\n",
|
||||
qtype, next_ino - 1);
|
||||
}
|
||||
|
||||
if (total_zones <= 6) {
|
||||
MSG(1, "\tError: %d zones: Need more zones "
|
||||
|
Loading…
Reference in New Issue
Block a user