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