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