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