quotaio.c revision 36e4e21f511423450285568ceb26b3ddb233e286
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, loff_t offset, 137 void *buf, unsigned int size) 138{ 139 ext2_file_t e2_file = qf->e2_file; 140 unsigned int bytes_written = 0; 141 errcode_t err; 142 143 err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL); 144 if (err) { 145 log_err("ext2fs_file_llseek failed: %ld", err); 146 return 0; 147 } 148 149 err = ext2fs_file_write(e2_file, buf, size, &bytes_written); 150 if (err) { 151 log_err("ext2fs_file_write failed: %ld", err); 152 return 0; 153 } 154 155 /* Correct inode.i_size is set in end_io. */ 156 return bytes_written; 157} 158 159static unsigned int quota_read_nomount(struct quota_file *qf, loff_t offset, 160 void *buf, unsigned int size) 161{ 162 ext2_file_t e2_file = qf->e2_file; 163 unsigned int bytes_read = 0; 164 errcode_t err; 165 166 err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL); 167 if (err) { 168 log_err("ext2fs_file_llseek failed: %ld", err); 169 return 0; 170 } 171 172 err = ext2fs_file_read(e2_file, buf, size, &bytes_read); 173 if (err) { 174 log_err("ext2fs_file_read failed: %ld", err); 175 return 0; 176 } 177 178 return bytes_read; 179} 180 181/* 182 * Detect quota format and initialize quota IO 183 */ 184struct quota_handle *init_io(ext2_filsys fs, const char *mntpt, int type, 185 int fmt, int flags) 186{ 187 log_err("Not Implemented.", ""); 188 BUG_ON(1); 189 return NULL; 190} 191 192static errcode_t init_new_quota_inode(ext2_filsys fs, ext2_ino_t ino) 193{ 194 struct ext2_inode inode; 195 errcode_t err = 0; 196 197 err = ext2fs_read_inode(fs, ino, &inode); 198 if (err) { 199 log_err("ex2fs_read_inode failed", ""); 200 return err; 201 } 202 203 if (EXT2_I_SIZE(&inode)) 204 truncate_quota_inode(fs, ino); 205 206 memset(&inode, 0, sizeof(struct ext2_inode)); 207 ext2fs_iblk_set(fs, &inode, 0); 208 inode.i_atime = inode.i_mtime = 209 inode.i_ctime = fs->now ? fs->now : time(0); 210 inode.i_links_count = 1; 211 inode.i_mode = LINUX_S_IFREG | 0600; 212 inode.i_flags |= EXT2_IMMUTABLE_FL; 213 if (fs->super->s_feature_incompat & 214 EXT3_FEATURE_INCOMPAT_EXTENTS) 215 inode.i_flags |= EXT4_EXTENTS_FL; 216 217 err = ext2fs_write_new_inode(fs, ino, &inode); 218 if (err) { 219 log_err("ext2fs_write_new_inode failed: %ld", err); 220 return err; 221 } 222 return err; 223} 224 225/* 226 * Create new quotafile of specified format on given filesystem 227 */ 228int new_io(struct quota_handle *h, ext2_filsys fs, int type, int fmt) 229{ 230 ext2_file_t e2_file; 231 int err; 232 unsigned long qf_inum; 233 234 if (fmt == -1) 235 fmt = QFMT_VFS_V1; 236 237 h->qh_qf.fs = fs; 238 if (type == USRQUOTA) 239 qf_inum = EXT4_USR_QUOTA_INO; 240 else if (type == GRPQUOTA) 241 qf_inum = EXT4_GRP_QUOTA_INO; 242 else 243 BUG_ON(1); 244 err = ext2fs_read_bitmaps(fs); 245 if (err) 246 goto out_err; 247 248 err = init_new_quota_inode(fs, qf_inum); 249 if (err) { 250 log_err("init_new_quota_inode failed", ""); 251 goto out_err; 252 } 253 h->qh_qf.ino = qf_inum; 254 h->e2fs_write = quota_write_nomount; 255 h->e2fs_read = quota_read_nomount; 256 257 log_debug("Creating quota ino=%lu, type=%d", qf_inum, type); 258 err = ext2fs_file_open(fs, qf_inum, 259 EXT2_FILE_WRITE | EXT2_FILE_CREATE, &e2_file); 260 if (err) { 261 log_err("ext2fs_file_open failed: %d", err); 262 goto out_err; 263 } 264 h->qh_qf.e2_file = e2_file; 265 266 h->qh_io_flags = 0; 267 h->qh_type = type; 268 h->qh_fmt = fmt; 269 memset(&h->qh_info, 0, sizeof(h->qh_info)); 270 h->qh_ops = "afile_ops_2; 271 272 if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) { 273 log_err("qh_ops->new_io failed", ""); 274 goto out_err1; 275 } 276 277 return 0; 278 279out_err1: 280 ext2fs_file_close(e2_file); 281out_err: 282 283 if (qf_inum) 284 truncate_quota_inode(fs, qf_inum); 285 286 return -1; 287} 288 289/* 290 * Close quotafile and release handle 291 */ 292int end_io(struct quota_handle *h) 293{ 294 if (h->qh_io_flags & IOFL_INFODIRTY) { 295 if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0) 296 return -1; 297 h->qh_io_flags &= ~IOFL_INFODIRTY; 298 } 299 300 if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0) 301 return -1; 302 if (h->qh_qf.e2_file) { 303 ext2fs_file_flush(h->qh_qf.e2_file); 304 ext2fs_file_set_size2(h->qh_qf.e2_file, 305 compute_inode_size(h->qh_qf.fs, h->qh_qf.ino)); 306 ext2fs_file_close(h->qh_qf.e2_file); 307 } 308 309 return 0; 310} 311 312/* 313 * Create empty quota structure 314 */ 315struct dquot *get_empty_dquot(void) 316{ 317 struct dquot *dquot = smalloc(sizeof(struct dquot)); 318 319 memset(dquot, 0, sizeof(*dquot)); 320 dquot->dq_id = -1; 321 return dquot; 322} 323