Whamcloud - gitweb
Shorten compile commands run by the build system
[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 #include <asm/byteorder.h>
18
19 #include "common.h"
20 #include "quotaio.h"
21
22 static const char extensions[MAXQUOTAS + 2][20] = INITQFNAMES;
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 static const char * const fmtnames[] = {
32         "vfsold",
33         "vfsv0",
34         "vfsv1",
35         "rpc",
36         "xfs"
37 };
38
39 /* Header in all newer quotafiles */
40 struct disk_dqheader {
41         u_int32_t dqh_magic;
42         u_int32_t dqh_version;
43 } __attribute__ ((packed));
44
45 /**
46  * Convert type of quota to written representation
47  */
48 const char *type2name(int type)
49 {
50         return extensions[type];
51 }
52
53 /**
54  * Creates a quota file name for given type and format.
55  */
56 const char *get_qf_name(int type, int fmt, char *buf)
57 {
58         BUG_ON(!buf);
59         snprintf(buf, PATH_MAX, "%s.%s",
60                  basenames[fmt], extensions[type]);
61
62         return buf;
63 }
64
65 const char *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[PATH_MAX] = {0};
70
71         BUG_ON(!mntpt);
72         BUG_ON(!path_buf);
73         BUG_ON(!path_buf_size);
74
75         strncpy(path_buf, mntpt, path_buf_size);
76         strncat(path_buf, "/", 1);
77         strncat(path_buf, get_qf_name(qtype, fmt, qf_name),
78                 path_buf_size - strlen(path_buf));
79
80         return path_buf;
81 }
82
83 /*
84  * Set grace time if needed
85  */
86 void update_grace_times(struct dquot *q)
87 {
88         time_t now;
89
90         time(&now);
91         if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) >
92                         q->dq_dqb.dqb_bsoftlimit) {
93                 if (!q->dq_dqb.dqb_btime)
94                         q->dq_dqb.dqb_btime =
95                                 now + q->dq_h->qh_info.dqi_bgrace;
96         } else {
97                 q->dq_dqb.dqb_btime = 0;
98         }
99
100         if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes >
101                         q->dq_dqb.dqb_isoftlimit) {
102                 if (!q->dq_dqb.dqb_itime)
103                                 q->dq_dqb.dqb_itime =
104                                         now + q->dq_h->qh_info.dqi_igrace;
105         } else {
106                 q->dq_dqb.dqb_itime = 0;
107         }
108 }
109
110 static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
111                                e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
112                                blk64_t ref_block EXT2FS_ATTR((unused)),
113                                int ref_offset EXT2FS_ATTR((unused)),
114                                void *private EXT2FS_ATTR((unused)))
115 {
116         blk64_t block;
117
118         block = *blocknr;
119         ext2fs_block_alloc_stats2(fs, block, -1);
120         return 0;
121 }
122
123 static int compute_num_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
124                                e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
125                                blk64_t ref_block EXT2FS_ATTR((unused)),
126                                int ref_offset EXT2FS_ATTR((unused)),
127                                void *private)
128 {
129         blk64_t block;
130         blk64_t *num_blocks = private;
131
132         *num_blocks += 1;
133         return 0;
134 }
135
136 void truncate_quota_inode(ext2_filsys fs, ext2_ino_t ino)
137 {
138         struct ext2_inode inode;
139         int i;
140
141         if (ext2fs_read_inode(fs, ino, &inode))
142                 return;
143
144         inode.i_dtime = fs->now ? fs->now : time(0);
145         if (!ext2fs_inode_has_valid_blocks(&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         ext2fs_write_inode(fs, ino, &inode);
153 }
154
155 static ext2_off64_t compute_inode_size(ext2_filsys fs, ext2_ino_t ino)
156 {
157         struct ext2_inode inode;
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, loff_t offset,
170                                         void *buf, unsigned int size)
171 {
172         ext2_file_t     e2_file = qf->e2_file;
173         unsigned int    bytes_written = 0;
174         errcode_t       err;
175
176         err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL);
177         if (err) {
178                 log_err("ext2fs_file_llseek failed: %ld", err);
179                 return 0;
180         }
181
182         err = ext2fs_file_write(e2_file, buf, size, &bytes_written);
183         if (err) {
184                 log_err("ext2fs_file_write failed: %ld", err);
185                 return 0;
186         }
187
188         /* Correct inode.i_size is set in end_io. */
189         return bytes_written;
190 }
191
192 static unsigned int quota_read_nomount(struct quota_file *qf, loff_t offset,
193                                         void *buf, unsigned int size)
194 {
195         ext2_file_t     e2_file = qf->e2_file;
196         unsigned int    bytes_read = 0;
197         errcode_t       err;
198
199         err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL);
200         if (err) {
201                 log_err("ext2fs_file_llseek failed: %ld", err);
202                 return 0;
203         }
204
205         err = ext2fs_file_read(e2_file, buf, size, &bytes_read);
206         if (err) {
207                 log_err("ext2fs_file_read failed: %ld", err);
208                 return 0;
209         }
210
211         return bytes_read;
212 }
213
214 /*
215  * Detect quota format and initialize quota IO
216  */
217 struct quota_handle *init_io(ext2_filsys fs, const char *mntpt, int type,
218                              int fmt, int flags)
219 {
220         log_err("Not Implemented.", "");
221         BUG_ON(1);
222         return NULL;
223 }
224
225 static errcode_t init_new_quota_inode(ext2_filsys fs, ext2_ino_t ino)
226 {
227         struct ext2_inode inode;
228         errcode_t err = 0;
229
230         err = ext2fs_read_inode(fs, ino, &inode);
231         if (err) {
232                 log_err("ex2fs_read_inode failed", "");
233                 return err;
234         }
235
236         if (EXT2_I_SIZE(&inode))
237                 truncate_quota_inode(fs, ino);
238
239         memset(&inode, 0, sizeof(struct ext2_inode));
240         ext2fs_iblk_set(fs, &inode, 0);
241         inode.i_atime = inode.i_mtime =
242                 inode.i_ctime = fs->now ? fs->now : time(0);
243         inode.i_links_count = 1;
244         inode.i_mode = LINUX_S_IFREG | 0600;
245         inode.i_flags |= EXT2_IMMUTABLE_FL;
246         if (fs->super->s_feature_incompat &
247                         EXT3_FEATURE_INCOMPAT_EXTENTS)
248                 inode.i_flags |= EXT4_EXTENTS_FL;
249
250         err = ext2fs_write_new_inode(fs, ino, &inode);
251         if (err) {
252                 log_err("ext2fs_write_new_inode failed: %ld", err);
253                 return err;
254         }
255         return err;
256 }
257
258 /*
259  * Create new quotafile of specified format on given filesystem
260  */
261 int new_io(struct quota_handle *h, ext2_filsys fs, int type, int fmt)
262 {
263         int fd = 0;
264         ext2_file_t e2_file;
265         const char *mnt_fsname;
266         char qf_name[PATH_MAX];
267         int err;
268         struct ext2_inode inode;
269         unsigned long qf_inum;
270         struct stat st;
271
272         if (fmt == -1)
273                 fmt = QFMT_VFS_V1;
274
275         h->qh_qf.fs = fs;
276         if (type == USRQUOTA)
277                 qf_inum = EXT4_USR_QUOTA_INO;
278         else if (type == GRPQUOTA)
279                 qf_inum = EXT4_GRP_QUOTA_INO;
280         else
281                 BUG_ON(1);
282         err = ext2fs_read_bitmaps(fs);
283         if (err)
284                 goto out_err;
285
286         err = init_new_quota_inode(fs, qf_inum);
287         if (err) {
288                 log_err("init_new_quota_inode failed", "");
289                 goto out_err;
290         }
291         h->qh_qf.ino = qf_inum;
292         h->e2fs_write = quota_write_nomount;
293         h->e2fs_read = quota_read_nomount;
294
295         log_debug("Creating quota ino=%lu, type=%d", qf_inum, type);
296         err = ext2fs_file_open(fs, qf_inum,
297                         EXT2_FILE_WRITE | EXT2_FILE_CREATE, &e2_file);
298         if (err) {
299                 log_err("ext2fs_file_open failed: %d", err);
300                 goto out_err;
301         }
302         h->qh_qf.e2_file = e2_file;
303
304         h->qh_io_flags = 0;
305         h->qh_type = type;
306         h->qh_fmt = fmt;
307         memset(&h->qh_info, 0, sizeof(h->qh_info));
308         h->qh_ops = &quotafile_ops_2;
309
310         if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) {
311                 log_err("qh_ops->new_io failed", "");
312                 goto out_err1;
313         }
314
315         return 0;
316
317 out_err1:
318         ext2fs_file_close(e2_file);
319 out_err:
320
321         if (qf_inum)
322                 truncate_quota_inode(fs, qf_inum);
323
324         return -1;
325 }
326
327 /*
328  * Close quotafile and release handle
329  */
330 int end_io(struct quota_handle *h)
331 {
332         if (h->qh_io_flags & IOFL_INFODIRTY) {
333                 if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
334                         return -1;
335                 h->qh_io_flags &= ~IOFL_INFODIRTY;
336         }
337
338         if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
339                 return -1;
340         if (h->qh_qf.e2_file) {
341                 ext2fs_file_flush(h->qh_qf.e2_file);
342                 ext2fs_file_set_size2(h->qh_qf.e2_file,
343                         compute_inode_size(h->qh_qf.fs, h->qh_qf.ino));
344                 ext2fs_file_close(h->qh_qf.e2_file);
345         }
346
347         return 0;
348 }
349
350 /*
351  * Create empty quota structure
352  */
353 struct dquot *get_empty_dquot(void)
354 {
355         struct dquot *dquot = smalloc(sizeof(struct dquot));
356
357         memset(dquot, 0, sizeof(*dquot));
358         dquot->dq_id = -1;
359         return dquot;
360 }