quotaio.c revision d6120a2a5e9825557fb36cddb028fe5d4b00afec
1/** quotaio.c 2 * 3 * Generic IO operations on quotafiles 4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR 5 * Aditya Kali <adityakali@google.com> - Ported to e2fsprogs 6 */ 7 8#include "config.h" 9#include <stdio.h> 10#include <errno.h> 11#include <string.h> 12#include <unistd.h> 13#include <stdlib.h> 14#include <sys/types.h> 15#include <sys/stat.h> 16#include <sys/file.h> 17 18#include "common.h" 19#include "quotaio.h" 20 21static const char extensions[MAXQUOTAS + 2][20] = INITQFNAMES; 22static const char * const basenames[] = { 23 "", /* undefined */ 24 "quota", /* QFMT_VFS_OLD */ 25 "aquota", /* QFMT_VFS_V0 */ 26 "", /* QFMT_OCFS2 */ 27 "aquota" /* QFMT_VFS_V1 */ 28}; 29 30static const char * const fmtnames[] = { 31 "vfsold", 32 "vfsv0", 33 "vfsv1", 34 "rpc", 35 "xfs" 36}; 37 38/* Header in all newer quotafiles */ 39struct disk_dqheader { 40 u_int32_t dqh_magic; 41 u_int32_t dqh_version; 42} __attribute__ ((packed)); 43 44/** 45 * Convert type of quota to written representation 46 */ 47const char *type2name(int type) 48{ 49 return extensions[type]; 50} 51 52/* 53 * Set grace time if needed 54 */ 55void update_grace_times(struct dquot *q) 56{ 57 time_t now; 58 59 time(&now); 60 if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) > 61 q->dq_dqb.dqb_bsoftlimit) { 62 if (!q->dq_dqb.dqb_btime) 63 q->dq_dqb.dqb_btime = 64 now + q->dq_h->qh_info.dqi_bgrace; 65 } else { 66 q->dq_dqb.dqb_btime = 0; 67 } 68 69 if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes > 70 q->dq_dqb.dqb_isoftlimit) { 71 if (!q->dq_dqb.dqb_itime) 72 q->dq_dqb.dqb_itime = 73 now + q->dq_h->qh_info.dqi_igrace; 74 } else { 75 q->dq_dqb.dqb_itime = 0; 76 } 77} 78 79static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr, 80 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), 81 blk64_t ref_block EXT2FS_ATTR((unused)), 82 int ref_offset EXT2FS_ATTR((unused)), 83 void *private EXT2FS_ATTR((unused))) 84{ 85 blk64_t block; 86 87 block = *blocknr; 88 ext2fs_block_alloc_stats2(fs, block, -1); 89 return 0; 90} 91 92static int compute_num_blocks_proc(ext2_filsys fs, blk64_t *blocknr, 93 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), 94 blk64_t ref_block EXT2FS_ATTR((unused)), 95 int ref_offset EXT2FS_ATTR((unused)), 96 void *private) 97{ 98 blk64_t block; 99 blk64_t *num_blocks = private; 100 101 *num_blocks += 1; 102 return 0; 103} 104 105void truncate_quota_inode(ext2_filsys fs, ext2_ino_t ino) 106{ 107 struct ext2_inode inode; 108 109 if (ext2fs_read_inode(fs, ino, &inode)) 110 return; 111 112 inode.i_dtime = fs->now ? fs->now : time(0); 113 if (!ext2fs_inode_has_valid_blocks(&inode)) 114 return; 115 116 ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, NULL, 117 release_blocks_proc, NULL); 118 119 memset(&inode, 0, sizeof(struct ext2_inode)); 120 ext2fs_write_inode(fs, ino, &inode); 121} 122 123static ext2_off64_t compute_inode_size(ext2_filsys fs, ext2_ino_t ino) 124{ 125 blk64_t num_blocks = 0; 126 127 ext2fs_block_iterate3(fs, ino, 128 BLOCK_FLAG_READ_ONLY, 129 NULL, 130 compute_num_blocks_proc, 131 &num_blocks); 132 return num_blocks * fs->blocksize; 133} 134 135/* Functions to read/write quota file. */ 136static unsigned int quota_write_nomount(struct quota_file *qf, 137 ext2_loff_t offset, 138 void *buf, unsigned int size) 139{ 140 ext2_file_t e2_file = qf->e2_file; 141 unsigned int bytes_written = 0; 142 errcode_t err; 143 144 err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL); 145 if (err) { 146 log_err("ext2fs_file_llseek failed: %ld", err); 147 return 0; 148 } 149 150 err = ext2fs_file_write(e2_file, buf, size, &bytes_written); 151 if (err) { 152 log_err("ext2fs_file_write failed: %ld", err); 153 return 0; 154 } 155 156 /* Correct inode.i_size is set in end_io. */ 157 return bytes_written; 158} 159 160static unsigned int quota_read_nomount(struct quota_file *qf, 161 ext2_loff_t offset, 162 void *buf, unsigned int size) 163{ 164 ext2_file_t e2_file = qf->e2_file; 165 unsigned int bytes_read = 0; 166 errcode_t err; 167 168 err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL); 169 if (err) { 170 log_err("ext2fs_file_llseek failed: %ld", err); 171 return 0; 172 } 173 174 err = ext2fs_file_read(e2_file, buf, size, &bytes_read); 175 if (err) { 176 log_err("ext2fs_file_read failed: %ld", err); 177 return 0; 178 } 179 180 return bytes_read; 181} 182 183/* 184 * Detect quota format and initialize quota IO 185 */ 186struct quota_handle *init_io(ext2_filsys fs, const char *mntpt, int type, 187 int fmt, int flags) 188{ 189 log_err("Not Implemented.", ""); 190 BUG_ON(1); 191 return NULL; 192} 193 194static errcode_t init_new_quota_inode(ext2_filsys fs, ext2_ino_t ino) 195{ 196 struct ext2_inode inode; 197 errcode_t err = 0; 198 199 err = ext2fs_read_inode(fs, ino, &inode); 200 if (err) { 201 log_err("ex2fs_read_inode failed", ""); 202 return err; 203 } 204 205 if (EXT2_I_SIZE(&inode)) 206 truncate_quota_inode(fs, ino); 207 208 memset(&inode, 0, sizeof(struct ext2_inode)); 209 ext2fs_iblk_set(fs, &inode, 0); 210 inode.i_atime = inode.i_mtime = 211 inode.i_ctime = fs->now ? fs->now : time(0); 212 inode.i_links_count = 1; 213 inode.i_mode = LINUX_S_IFREG | 0600; 214 inode.i_flags |= EXT2_IMMUTABLE_FL; 215 if (fs->super->s_feature_incompat & 216 EXT3_FEATURE_INCOMPAT_EXTENTS) 217 inode.i_flags |= EXT4_EXTENTS_FL; 218 219 err = ext2fs_write_new_inode(fs, ino, &inode); 220 if (err) { 221 log_err("ext2fs_write_new_inode failed: %ld", err); 222 return err; 223 } 224 return err; 225} 226 227/* 228 * Create new quotafile of specified format on given filesystem 229 */ 230int new_io(struct quota_handle *h, ext2_filsys fs, int type, int fmt) 231{ 232 ext2_file_t e2_file; 233 int err; 234 unsigned long qf_inum; 235 236 if (fmt == -1) 237 fmt = QFMT_VFS_V1; 238 239 h->qh_qf.fs = fs; 240 if (type == USRQUOTA) 241 qf_inum = EXT4_USR_QUOTA_INO; 242 else if (type == GRPQUOTA) 243 qf_inum = EXT4_GRP_QUOTA_INO; 244 else 245 BUG_ON(1); 246 err = ext2fs_read_bitmaps(fs); 247 if (err) 248 goto out_err; 249 250 err = init_new_quota_inode(fs, qf_inum); 251 if (err) { 252 log_err("init_new_quota_inode failed", ""); 253 goto out_err; 254 } 255 h->qh_qf.ino = qf_inum; 256 h->e2fs_write = quota_write_nomount; 257 h->e2fs_read = quota_read_nomount; 258 259 log_debug("Creating quota ino=%lu, type=%d", qf_inum, type); 260 err = ext2fs_file_open(fs, qf_inum, 261 EXT2_FILE_WRITE | EXT2_FILE_CREATE, &e2_file); 262 if (err) { 263 log_err("ext2fs_file_open failed: %d", err); 264 goto out_err; 265 } 266 h->qh_qf.e2_file = e2_file; 267 268 h->qh_io_flags = 0; 269 h->qh_type = type; 270 h->qh_fmt = fmt; 271 memset(&h->qh_info, 0, sizeof(h->qh_info)); 272 h->qh_ops = "afile_ops_2; 273 274 if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) { 275 log_err("qh_ops->new_io failed", ""); 276 goto out_err1; 277 } 278 279 return 0; 280 281out_err1: 282 ext2fs_file_close(e2_file); 283out_err: 284 285 if (qf_inum) 286 truncate_quota_inode(fs, qf_inum); 287 288 return -1; 289} 290 291/* 292 * Close quotafile and release handle 293 */ 294int end_io(struct quota_handle *h) 295{ 296 if (h->qh_io_flags & IOFL_INFODIRTY) { 297 if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0) 298 return -1; 299 h->qh_io_flags &= ~IOFL_INFODIRTY; 300 } 301 302 if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0) 303 return -1; 304 if (h->qh_qf.e2_file) { 305 ext2fs_file_flush(h->qh_qf.e2_file); 306 ext2fs_file_set_size2(h->qh_qf.e2_file, 307 compute_inode_size(h->qh_qf.fs, h->qh_qf.ino)); 308 ext2fs_file_close(h->qh_qf.e2_file); 309 } 310 311 return 0; 312} 313 314/* 315 * Create empty quota structure 316 */ 317struct dquot *get_empty_dquot(void) 318{ 319 struct dquot *dquot = smalloc(sizeof(struct dquot)); 320 321 memset(dquot, 0, sizeof(*dquot)); 322 dquot->dq_id = -1; 323 return dquot; 324} 325