Whamcloud - gitweb
libquota: quiet log_err() bad format warnings
[tools/e2fsprogs.git] / lib / quota / quotaio.c
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
22 static const char * const extensions[MAXQUOTAS] = {"user", "group"};
23 static 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 */
32 struct 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  */
40 const char *type2name(int type)
41 {
42         return extensions[type];
43 }
44
45 /**
46  * Creates a quota file name for given type and format.
47  */
48 const 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
58 const 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  */
77 void 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
101 static 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
114 static 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 *num_blocks = private;
121
122         *num_blocks += 1;
123         return 0;
124 }
125
126 errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino)
127 {
128         struct ext2_inode inode;
129         errcode_t err;
130
131         if ((err = ext2fs_read_inode(fs, ino, &inode)))
132                 return err;
133
134         if ((ino == EXT4_USR_QUOTA_INO) || (ino == EXT4_GRP_QUOTA_INO)) {
135                 inode.i_dtime = fs->now ? fs->now : time(0);
136                 if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
137                         return 0;
138
139                 ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, NULL,
140                                       release_blocks_proc, NULL);
141                 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
142                 memset(&inode, 0, sizeof(struct ext2_inode));
143         } else {
144                 inode.i_flags &= ~EXT2_IMMUTABLE_FL;
145         }
146         err = ext2fs_write_inode(fs, ino, &inode);
147         return err;
148 }
149
150 static ext2_off64_t compute_inode_size(ext2_filsys fs, ext2_ino_t ino)
151 {
152         blk64_t num_blocks = 0;
153
154         ext2fs_block_iterate3(fs, ino,
155                               BLOCK_FLAG_READ_ONLY,
156                               NULL,
157                               compute_num_blocks_proc,
158                               &num_blocks);
159         return num_blocks * fs->blocksize;
160 }
161
162 /* Functions to read/write quota file. */
163 static unsigned int quota_write_nomount(struct quota_file *qf,
164                                         ext2_loff_t offset,
165                                         void *buf, unsigned int size)
166 {
167         ext2_file_t     e2_file = qf->e2_file;
168         unsigned int    bytes_written = 0;
169         errcode_t       err;
170
171         err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL);
172         if (err) {
173                 log_err("ext2fs_file_llseek failed: %ld", err);
174                 return 0;
175         }
176
177         err = ext2fs_file_write(e2_file, buf, size, &bytes_written);
178         if (err) {
179                 log_err("ext2fs_file_write failed: %ld", err);
180                 return 0;
181         }
182
183         /* Correct inode.i_size is set in end_io. */
184         return bytes_written;
185 }
186
187 static unsigned int quota_read_nomount(struct quota_file *qf,
188                                        ext2_loff_t offset,
189                                        void *buf, unsigned int size)
190 {
191         ext2_file_t     e2_file = qf->e2_file;
192         unsigned int    bytes_read = 0;
193         errcode_t       err;
194
195         err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL);
196         if (err) {
197                 log_err("ext2fs_file_llseek failed: %ld", err);
198                 return 0;
199         }
200
201         err = ext2fs_file_read(e2_file, buf, size, &bytes_read);
202         if (err) {
203                 log_err("ext2fs_file_read failed: %ld", err);
204                 return 0;
205         }
206
207         return bytes_read;
208 }
209
210 /*
211  * Detect quota format and initialize quota IO
212  */
213 errcode_t quota_file_open(struct quota_handle *h, ext2_filsys fs,
214                           ext2_ino_t qf_ino, int type, int fmt, int flags)
215 {
216         ext2_file_t e2_file;
217         errcode_t err;
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         log_debug("Opening quota ino=%lu, type=%d", qf_ino, type);
227         err = ext2fs_file_open(fs, qf_ino, flags, &e2_file);
228         if (err) {
229                 log_err("ext2fs_file_open failed: %s", error_message(err));
230                 return err;
231         }
232         h->qh_qf.e2_file = e2_file;
233
234         h->qh_qf.fs = fs;
235         h->qh_qf.ino = qf_ino;
236         h->e2fs_write = quota_write_nomount;
237         h->e2fs_read = quota_read_nomount;
238         h->qh_io_flags = 0;
239         h->qh_type = type;
240         h->qh_fmt = fmt;
241         memset(&h->qh_info, 0, sizeof(h->qh_info));
242         h->qh_ops = &quotafile_ops_2;
243
244         if (h->qh_ops->check_file &&
245             (h->qh_ops->check_file(h, type, fmt) == 0)) {
246                 log_err("qh_ops->check_file failed");
247                 ext2fs_file_close(e2_file);
248                 return -1;
249         }
250
251         if (h->qh_ops->init_io && (h->qh_ops->init_io(h) < 0)) {
252                 log_err("qh_ops->init_io failed");
253                 ext2fs_file_close(e2_file);
254                 return -1;
255         }
256
257         return 0;
258 }
259
260 static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
261 {
262         struct ext2_inode inode;
263         errcode_t err = 0;
264
265         err = ext2fs_read_inode(fs, ino, &inode);
266         if (err) {
267                 log_err("ex2fs_read_inode failed");
268                 return err;
269         }
270
271         if (EXT2_I_SIZE(&inode))
272                 quota_inode_truncate(fs, ino);
273
274         memset(&inode, 0, sizeof(struct ext2_inode));
275         ext2fs_iblk_set(fs, &inode, 0);
276         inode.i_atime = inode.i_mtime =
277                 inode.i_ctime = fs->now ? fs->now : time(0);
278         inode.i_links_count = 1;
279         inode.i_mode = LINUX_S_IFREG | 0600;
280         inode.i_flags |= EXT2_IMMUTABLE_FL;
281         if (fs->super->s_feature_incompat &
282                         EXT3_FEATURE_INCOMPAT_EXTENTS)
283                 inode.i_flags |= EXT4_EXTENTS_FL;
284
285         err = ext2fs_write_new_inode(fs, ino, &inode);
286         if (err) {
287                 log_err("ext2fs_write_new_inode failed: %ld", err);
288                 return err;
289         }
290         return err;
291 }
292
293 /*
294  * Create new quotafile of specified format on given filesystem
295  */
296 errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, int type, int fmt)
297 {
298         ext2_file_t e2_file;
299         int err;
300         unsigned long qf_inum;
301
302         if (fmt == -1)
303                 fmt = QFMT_VFS_V1;
304
305         h->qh_qf.fs = fs;
306         if (type == USRQUOTA)
307                 qf_inum = EXT4_USR_QUOTA_INO;
308         else if (type == GRPQUOTA)
309                 qf_inum = EXT4_GRP_QUOTA_INO;
310         else
311                 return -1;
312
313         err = ext2fs_read_bitmaps(fs);
314         if (err)
315                 goto out_err;
316
317         err = quota_inode_init_new(fs, qf_inum);
318         if (err) {
319                 log_err("init_new_quota_inode failed");
320                 goto out_err;
321         }
322         h->qh_qf.ino = qf_inum;
323         h->e2fs_write = quota_write_nomount;
324         h->e2fs_read = quota_read_nomount;
325
326         log_debug("Creating quota ino=%lu, type=%d", qf_inum, type);
327         err = ext2fs_file_open(fs, qf_inum,
328                         EXT2_FILE_WRITE | EXT2_FILE_CREATE, &e2_file);
329         if (err) {
330                 log_err("ext2fs_file_open failed: %d", err);
331                 goto out_err;
332         }
333         h->qh_qf.e2_file = e2_file;
334
335         h->qh_io_flags = 0;
336         h->qh_type = type;
337         h->qh_fmt = fmt;
338         memset(&h->qh_info, 0, sizeof(h->qh_info));
339         h->qh_ops = &quotafile_ops_2;
340
341         if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) {
342                 log_err("qh_ops->new_io failed");
343                 goto out_err1;
344         }
345
346         return 0;
347
348 out_err1:
349         ext2fs_file_close(e2_file);
350 out_err:
351
352         if (qf_inum)
353                 quota_inode_truncate(fs, qf_inum);
354
355         return -1;
356 }
357
358 /*
359  * Close quotafile and release handle
360  */
361 errcode_t quota_file_close(struct quota_handle *h)
362 {
363         if (h->qh_io_flags & IOFL_INFODIRTY) {
364                 if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
365                         return -1;
366                 h->qh_io_flags &= ~IOFL_INFODIRTY;
367         }
368
369         if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
370                 return -1;
371         if (h->qh_qf.e2_file) {
372                 ext2fs_file_flush(h->qh_qf.e2_file);
373                 ext2fs_file_set_size2(h->qh_qf.e2_file,
374                         compute_inode_size(h->qh_qf.fs, h->qh_qf.ino));
375                 ext2fs_file_close(h->qh_qf.e2_file);
376         }
377
378         return 0;
379 }
380
381 /*
382  * Create empty quota structure
383  */
384 struct dquot *get_empty_dquot(void)
385 {
386         struct dquot *dquot;
387
388         if (ext2fs_get_memzero(sizeof(struct dquot), &dquot)) {
389                 log_err("Failed to allocate dquot");
390                 return NULL;
391         }
392
393         dquot->dq_id = -1;
394         return dquot;
395 }