quotaio.c revision 2b8d68391957dfb681931da42c42416a9525b395
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 * Creates a quota file name for given type and format. 54 */ 55const char *quota_get_qf_name(int type, int fmt, char *buf) 56{ 57 if (!buf) 58 return NULL; 59 snprintf(buf, PATH_MAX, "%s.%s", 60 basenames[fmt], extensions[type]); 61 62 return buf; 63} 64 65const char *quota_get_qf_path(const char *mntpt, int qtype, int fmt, 66 char *path_buf, size_t path_buf_size) 67{ 68 struct stat qf_stat; 69 char qf_name[PATH_MAX] = {0}; 70 71 if (!mntpt || !path_buf || !path_buf_size) 72 return NULL; 73 74 strncpy(path_buf, mntpt, path_buf_size); 75 strncat(path_buf, "/", 1); 76 strncat(path_buf, quota_get_qf_name(qtype, fmt, qf_name), 77 path_buf_size - strlen(path_buf)); 78 79 return path_buf; 80} 81 82/* 83 * Set grace time if needed 84 */ 85void update_grace_times(struct dquot *q) 86{ 87 time_t now; 88 89 time(&now); 90 if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) > 91 q->dq_dqb.dqb_bsoftlimit) { 92 if (!q->dq_dqb.dqb_btime) 93 q->dq_dqb.dqb_btime = 94 now + q->dq_h->qh_info.dqi_bgrace; 95 } else { 96 q->dq_dqb.dqb_btime = 0; 97 } 98 99 if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes > 100 q->dq_dqb.dqb_isoftlimit) { 101 if (!q->dq_dqb.dqb_itime) 102 q->dq_dqb.dqb_itime = 103 now + q->dq_h->qh_info.dqi_igrace; 104 } else { 105 q->dq_dqb.dqb_itime = 0; 106 } 107} 108 109static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr, 110 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), 111 blk64_t ref_block EXT2FS_ATTR((unused)), 112 int ref_offset EXT2FS_ATTR((unused)), 113 void *private EXT2FS_ATTR((unused))) 114{ 115 blk64_t block; 116 117 block = *blocknr; 118 ext2fs_block_alloc_stats2(fs, block, -1); 119 return 0; 120} 121 122static int compute_num_blocks_proc(ext2_filsys fs, blk64_t *blocknr, 123 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), 124 blk64_t ref_block EXT2FS_ATTR((unused)), 125 int ref_offset EXT2FS_ATTR((unused)), 126 void *private) 127{ 128 blk64_t block; 129 blk64_t *num_blocks = private; 130 131 *num_blocks += 1; 132 return 0; 133} 134 135errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino) 136{ 137 struct ext2_inode inode; 138 errcode_t err; 139 int i; 140 141 if ((err = ext2fs_read_inode(fs, ino, &inode))) 142 return err; 143 144 inode.i_dtime = fs->now ? fs->now : time(0); 145 if (!ext2fs_inode_has_valid_blocks2(fs, &inode)) 146 return; 147 148 ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, NULL, 149 release_blocks_proc, NULL); 150 151 memset(&inode, 0, sizeof(struct ext2_inode)); 152 err = ext2fs_write_inode(fs, ino, &inode); 153 return err; 154} 155 156static ext2_off64_t compute_inode_size(ext2_filsys fs, ext2_ino_t ino) 157{ 158 blk64_t num_blocks = 0; 159 160 ext2fs_block_iterate3(fs, ino, 161 BLOCK_FLAG_READ_ONLY, 162 NULL, 163 compute_num_blocks_proc, 164 &num_blocks); 165 return num_blocks * fs->blocksize; 166} 167 168/* Functions to read/write quota file. */ 169static unsigned int quota_write_nomount(struct quota_file *qf, 170 ext2_loff_t offset, 171 void *buf, unsigned int size) 172{ 173 ext2_file_t e2_file = qf->e2_file; 174 unsigned int bytes_written = 0; 175 errcode_t err; 176 177 err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL); 178 if (err) { 179 log_err("ext2fs_file_llseek failed: %ld", err); 180 return 0; 181 } 182 183 err = ext2fs_file_write(e2_file, buf, size, &bytes_written); 184 if (err) { 185 log_err("ext2fs_file_write failed: %ld", err); 186 return 0; 187 } 188 189 /* Correct inode.i_size is set in end_io. */ 190 return bytes_written; 191} 192 193static unsigned int quota_read_nomount(struct quota_file *qf, 194 ext2_loff_t offset, 195 void *buf, unsigned int size) 196{ 197 ext2_file_t e2_file = qf->e2_file; 198 unsigned int bytes_read = 0; 199 errcode_t err; 200 201 err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL); 202 if (err) { 203 log_err("ext2fs_file_llseek failed: %ld", err); 204 return 0; 205 } 206 207 err = ext2fs_file_read(e2_file, buf, size, &bytes_read); 208 if (err) { 209 log_err("ext2fs_file_read failed: %ld", err); 210 return 0; 211 } 212 213 return bytes_read; 214} 215 216/* 217 * Detect quota format and initialize quota IO 218 */ 219errcode_t quota_file_open(struct quota_handle *h, ext2_filsys fs, 220 ext2_ino_t qf_ino, int type, int fmt, int flags) 221{ 222 ext2_file_t e2_file; 223 errcode_t err; 224 225 if (fmt == -1) 226 fmt = QFMT_VFS_V1; 227 228 err = ext2fs_read_bitmaps(fs); 229 if (err) 230 return err; 231 232 log_debug("Opening quota ino=%lu, type=%d", qf_ino, type); 233 err = ext2fs_file_open(fs, qf_ino, flags, &e2_file); 234 if (err) { 235 log_err("ext2fs_file_open failed: %s", error_message(err)); 236 return err; 237 } 238 h->qh_qf.e2_file = e2_file; 239 240 h->qh_qf.fs = fs; 241 h->qh_qf.ino = qf_ino; 242 h->e2fs_write = quota_write_nomount; 243 h->e2fs_read = quota_read_nomount; 244 h->qh_io_flags = 0; 245 h->qh_type = type; 246 h->qh_fmt = fmt; 247 memset(&h->qh_info, 0, sizeof(h->qh_info)); 248 h->qh_ops = "afile_ops_2; 249 250 if (h->qh_ops->check_file && 251 (h->qh_ops->check_file(h, type, fmt) == 0)) { 252 log_err("qh_ops->check_file failed", ""); 253 ext2fs_file_close(e2_file); 254 return -1; 255 } 256 257 if (h->qh_ops->init_io && (h->qh_ops->init_io(h) < 0)) { 258 log_err("qh_ops->init_io failed", ""); 259 ext2fs_file_close(e2_file); 260 return -1; 261 } 262 263 return 0; 264} 265 266static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino) 267{ 268 struct ext2_inode inode; 269 errcode_t err = 0; 270 271 err = ext2fs_read_inode(fs, ino, &inode); 272 if (err) { 273 log_err("ex2fs_read_inode failed", ""); 274 return err; 275 } 276 277 if (EXT2_I_SIZE(&inode)) 278 quota_inode_truncate(fs, ino); 279 280 memset(&inode, 0, sizeof(struct ext2_inode)); 281 ext2fs_iblk_set(fs, &inode, 0); 282 inode.i_atime = inode.i_mtime = 283 inode.i_ctime = fs->now ? fs->now : time(0); 284 inode.i_links_count = 1; 285 inode.i_mode = LINUX_S_IFREG | 0600; 286 inode.i_flags |= EXT2_IMMUTABLE_FL; 287 if (fs->super->s_feature_incompat & 288 EXT3_FEATURE_INCOMPAT_EXTENTS) 289 inode.i_flags |= EXT4_EXTENTS_FL; 290 291 err = ext2fs_write_new_inode(fs, ino, &inode); 292 if (err) { 293 log_err("ext2fs_write_new_inode failed: %ld", err); 294 return err; 295 } 296 return err; 297} 298 299/* 300 * Create new quotafile of specified format on given filesystem 301 */ 302errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, int type, int fmt) 303{ 304 ext2_file_t e2_file; 305 int err; 306 unsigned long qf_inum; 307 308 if (fmt == -1) 309 fmt = QFMT_VFS_V1; 310 311 h->qh_qf.fs = fs; 312 if (type == USRQUOTA) 313 qf_inum = EXT4_USR_QUOTA_INO; 314 else if (type == GRPQUOTA) 315 qf_inum = EXT4_GRP_QUOTA_INO; 316 else 317 return -1; 318 319 err = ext2fs_read_bitmaps(fs); 320 if (err) 321 goto out_err; 322 323 err = quota_inode_init_new(fs, qf_inum); 324 if (err) { 325 log_err("init_new_quota_inode failed", ""); 326 goto out_err; 327 } 328 h->qh_qf.ino = qf_inum; 329 h->e2fs_write = quota_write_nomount; 330 h->e2fs_read = quota_read_nomount; 331 332 log_debug("Creating quota ino=%lu, type=%d", qf_inum, type); 333 err = ext2fs_file_open(fs, qf_inum, 334 EXT2_FILE_WRITE | EXT2_FILE_CREATE, &e2_file); 335 if (err) { 336 log_err("ext2fs_file_open failed: %d", err); 337 goto out_err; 338 } 339 h->qh_qf.e2_file = e2_file; 340 341 h->qh_io_flags = 0; 342 h->qh_type = type; 343 h->qh_fmt = fmt; 344 memset(&h->qh_info, 0, sizeof(h->qh_info)); 345 h->qh_ops = "afile_ops_2; 346 347 if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) { 348 log_err("qh_ops->new_io failed", ""); 349 goto out_err1; 350 } 351 352 return 0; 353 354out_err1: 355 ext2fs_file_close(e2_file); 356out_err: 357 358 if (qf_inum) 359 quota_inode_truncate(fs, qf_inum); 360 361 return -1; 362} 363 364/* 365 * Close quotafile and release handle 366 */ 367errcode_t quota_file_close(struct quota_handle *h) 368{ 369 if (h->qh_io_flags & IOFL_INFODIRTY) { 370 if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0) 371 return -1; 372 h->qh_io_flags &= ~IOFL_INFODIRTY; 373 } 374 375 if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0) 376 return -1; 377 if (h->qh_qf.e2_file) { 378 ext2fs_file_flush(h->qh_qf.e2_file); 379 ext2fs_file_set_size2(h->qh_qf.e2_file, 380 compute_inode_size(h->qh_qf.fs, h->qh_qf.ino)); 381 ext2fs_file_close(h->qh_qf.e2_file); 382 } 383 384 return 0; 385} 386 387/* 388 * Create empty quota structure 389 */ 390struct dquot *get_empty_dquot(void) 391{ 392 struct dquot *dquot; 393 394 if (ext2fs_get_memzero(sizeof(struct dquot), &dquot)) { 395 log_err("Failed to allocate dquot", ""); 396 return NULL; 397 } 398 399 dquot->dq_id = -1; 400 return dquot; 401} 402