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