Whamcloud - gitweb
libquota: don't include system quota.h header
[tools/e2fsprogs.git] / lib / quota / mkquota.c
1 /*
2  * mkquota.c --- create quota files for a filesystem
3  *
4  * Aditya Kali <adityakali@google.com>
5  */
6 #include "config.h"
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <fcntl.h>
13
14 #include "ext2fs/ext2_fs.h"
15 #include "ext2fs/ext2fs.h"
16 #include "e2p/e2p.h"
17
18 #include "quota.h"
19 #include "quotaio.h"
20 #include "quotaio_v2.h"
21 #include "quotaio_tree.h"
22 #include "mkquota.h"
23 #include "common.h"
24
25 /* Needed for architectures where sizeof(int) != sizeof(void *) */
26 #define UINT_TO_VOIDPTR(val)  ((void *)(intptr_t)(val))
27 #define VOIDPTR_TO_UINT(ptr)  ((unsigned int)(intptr_t)(ptr))
28
29 static void print_inode(struct ext2_inode *inode)
30 {
31         if (!inode)
32                 return;
33
34         fprintf(stderr, "  i_mode = %d\n", inode->i_mode);
35         fprintf(stderr, "  i_uid = %d\n", inode->i_uid);
36         fprintf(stderr, "  i_size = %d\n", inode->i_size);
37         fprintf(stderr, "  i_atime = %d\n", inode->i_atime);
38         fprintf(stderr, "  i_ctime = %d\n", inode->i_ctime);
39         fprintf(stderr, "  i_mtime = %d\n", inode->i_mtime);
40         fprintf(stderr, "  i_dtime = %d\n", inode->i_dtime);
41         fprintf(stderr, "  i_gid = %d\n", inode->i_gid);
42         fprintf(stderr, "  i_links_count = %d\n", inode->i_links_count);
43         fprintf(stderr, "  i_blocks = %d\n", inode->i_blocks);
44         fprintf(stderr, "  i_flags = %d\n", inode->i_flags);
45
46         return;
47 }
48
49 int quota_is_on(ext2_filsys fs, int type)
50 {
51         char tmp[1024];
52         qid_t id = (type == USRQUOTA) ? getuid() : getgid();
53
54 #ifdef HAVE_QUOTACTL
55         if (!quotactl(QCMD(Q_V2_GETQUOTA, type), fs->device_name, id, tmp))
56                 return 1;
57 #endif
58         return 0;
59 }
60
61 /*
62  * Returns 0 if not able to find the quota file, otherwise returns its
63  * inode number.
64  */
65 int quota_file_exists(ext2_filsys fs, int qtype, int fmt)
66 {
67         char qf_name[256];
68         errcode_t ret;
69         ext2_ino_t ino;
70
71         if (qtype >= MAXQUOTAS)
72                 return -EINVAL;
73
74         quota_get_qf_name(qtype, fmt, qf_name);
75
76         ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0,
77                             &ino);
78         if (ret)
79                 return 0;
80
81         return ino;
82 }
83
84 /*
85  * Set the value for reserved quota inode number field in superblock.
86  */
87 void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype)
88 {
89         ext2_ino_t *inump;
90
91         inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum :
92                 &fs->super->s_grp_quota_inum;
93
94         log_debug("setting quota ino in superblock: ino=%u, type=%d", ino,
95                  qtype);
96         *inump = ino;
97         ext2fs_mark_super_dirty(fs);
98 }
99
100 errcode_t quota_remove_inode(ext2_filsys fs, int qtype)
101 {
102         ext2_ino_t qf_ino;
103
104         ext2fs_read_bitmaps(fs);
105         qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum :
106                 fs->super->s_grp_quota_inum;
107         quota_set_sb_inum(fs, 0, qtype);
108         /* Truncate the inode only if its a reserved one. */
109         if (qf_ino < EXT2_FIRST_INODE(fs->super))
110                 quota_inode_truncate(fs, qf_ino);
111
112         ext2fs_mark_super_dirty(fs);
113         ext2fs_write_bitmaps(fs);
114         return 0;
115 }
116
117 static void write_dquots(dict_t *dict, struct quota_handle *qh)
118 {
119         dnode_t         *n;
120         struct dquot    *dq;
121
122         for (n = dict_first(dict); n; n = dict_next(dict, n)) {
123                 dq = dnode_get(n);
124                 if (dq) {
125                         dq->dq_h = qh;
126                         update_grace_times(dq);
127                         qh->qh_ops->commit_dquot(dq);
128                 }
129         }
130 }
131
132 errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
133 {
134         int             retval = 0, i;
135         dict_t          *dict;
136         ext2_filsys     fs;
137         struct quota_handle *h;
138         int             fmt = QFMT_VFS_V1;
139
140         if (!qctx)
141                 return 0;
142
143         fs = qctx->fs;
144         retval = ext2fs_get_mem(sizeof(struct quota_handle), &h);
145         if (retval) {
146                 log_err("Unable to allocate quota handle", "");
147                 goto out;
148         }
149
150         ext2fs_read_bitmaps(fs);
151
152         for (i = 0; i < MAXQUOTAS; i++) {
153                 if ((qtype != -1) && (i != qtype))
154                         continue;
155
156                 dict = qctx->quota_dict[i];
157                 if (!dict)
158                         continue;
159
160                 retval = quota_file_create(h, fs, i, fmt);
161                 if (retval < 0) {
162                         log_err("Cannot initialize io on quotafile", "");
163                         continue;
164                 }
165
166                 write_dquots(dict, h);
167                 retval = quota_file_close(h);
168                 if (retval < 0) {
169                         log_err("Cannot finish IO on new quotafile: %s",
170                                 strerror(errno));
171                         if (h->qh_qf.e2_file)
172                                 ext2fs_file_close(h->qh_qf.e2_file);
173                         quota_inode_truncate(fs, h->qh_qf.ino);
174                         continue;
175                 }
176
177                 /* Set quota inode numbers in superblock. */
178                 quota_set_sb_inum(fs, h->qh_qf.ino, i);
179                 ext2fs_mark_super_dirty(fs);
180                 ext2fs_mark_bb_dirty(fs);
181                 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
182         }
183
184         ext2fs_write_bitmaps(fs);
185 out:
186         if (h)
187                 ext2fs_free_mem(&h);
188         return retval;
189 }
190
191 /******************************************************************/
192 /* Helper functions for computing quota in memory.                */
193 /******************************************************************/
194
195 static int dict_uint_cmp(const void *a, const void *b)
196 {
197         unsigned int    c, d;
198
199         c = VOIDPTR_TO_UINT(a);
200         d = VOIDPTR_TO_UINT(b);
201
202         return c - d;
203 }
204
205 static inline qid_t get_qid(struct ext2_inode *inode, int qtype)
206 {
207         if (qtype == USRQUOTA)
208                 return inode_uid(*inode);
209         return inode_gid(*inode);
210 }
211
212 static void quota_dnode_free(dnode_t *node,
213                              void *context EXT2FS_ATTR((unused)))
214 {
215         void *ptr = node ? dnode_get(node) : 0;
216
217         ext2fs_free_mem(&ptr);
218         free(node);
219 }
220
221 /*
222  * Set up the quota tracking data structures.
223  */
224 errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype)
225 {
226         int     i, err = 0;
227         dict_t  *dict;
228         quota_ctx_t ctx;
229
230         err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx);
231         if (err) {
232                 log_err("Failed to allocate quota context", "");
233                 return err;
234         }
235
236         memset(ctx, 0, sizeof(struct quota_ctx));
237         for (i = 0; i < MAXQUOTAS; i++) {
238                 if ((qtype != -1) && (i != qtype))
239                         continue;
240                 err = ext2fs_get_mem(sizeof(dict_t), &dict);
241                 if (err) {
242                         log_err("Failed to allocate dictionary", "");
243                         return err;
244                 }
245                 ctx->quota_dict[i] = dict;
246                 dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
247                 dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
248         }
249
250         ctx->fs = fs;
251         *qctx = ctx;
252         return 0;
253 }
254
255 void quota_release_context(quota_ctx_t *qctx)
256 {
257         dict_t  *dict;
258         int     i;
259         quota_ctx_t ctx;
260
261         if (!qctx)
262                 return;
263
264         ctx = *qctx;
265         for (i = 0; i < MAXQUOTAS; i++) {
266                 dict = ctx->quota_dict[i];
267                 ctx->quota_dict[i] = 0;
268                 if (dict) {
269                         dict_free_nodes(dict);
270                         free(dict);
271                 }
272         }
273         *qctx = NULL;
274         free(ctx);
275 }
276
277 static struct dquot *get_dq(dict_t *dict, __u32 key)
278 {
279         struct dquot    *dq;
280         dnode_t         *n;
281
282         n = dict_lookup(dict, UINT_TO_VOIDPTR(key));
283         if (n)
284                 dq = dnode_get(n);
285         else {
286                 if (ext2fs_get_mem(sizeof(struct dquot), &dq)) {
287                         log_err("Unable to allocate dquot", "");
288                         return NULL;
289                 }
290                 memset(dq, 0, sizeof(struct dquot));
291                 dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq);
292                 dq->dq_id = key;
293         }
294         return dq;
295 }
296
297
298 /*
299  * Called to update the blocks used by a particular inode
300  */
301 void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
302                     qsize_t space)
303 {
304         struct dquot    *dq;
305         dict_t          *dict;
306         int             i;
307
308         if (!qctx)
309                 return;
310
311         log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
312                         inode_uid(*inode),
313                         inode_gid(*inode), space);
314         for (i = 0; i < MAXQUOTAS; i++) {
315                 dict = qctx->quota_dict[i];
316                 if (dict) {
317                         dq = get_dq(dict, get_qid(inode, i));
318                         if (dq)
319                                 dq->dq_dqb.dqb_curspace += space;
320                 }
321         }
322 }
323
324 /*
325  * Called to remove some blocks used by a particular inode
326  */
327 void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
328                     qsize_t space)
329 {
330         struct dquot    *dq;
331         dict_t          *dict;
332         int             i;
333
334         if (!qctx)
335                 return;
336
337         log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
338                         inode_uid(*inode),
339                         inode_gid(*inode), space);
340         for (i = 0; i < MAXQUOTAS; i++) {
341                 dict = qctx->quota_dict[i];
342                 if (dict) {
343                         dq = get_dq(dict, get_qid(inode, i));
344                         dq->dq_dqb.dqb_curspace -= space;
345                 }
346         }
347 }
348
349 /*
350  * Called to count the files used by an inode's user/group
351  */
352 void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode,
353                        ext2_ino_t ino, int adjust)
354 {
355         struct dquot    *dq;
356         dict_t          *dict;
357         int             i;
358
359         if (!qctx)
360                 return;
361
362         log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino,
363                         inode_uid(*inode),
364                         inode_gid(*inode), adjust);
365         for (i = 0; i < MAXQUOTAS; i++) {
366                 dict = qctx->quota_dict[i];
367                 if (dict) {
368                         dq = get_dq(dict, get_qid(inode, i));
369                         dq->dq_dqb.dqb_curinodes += adjust;
370                 }
371         }
372 }
373
374 errcode_t quota_compute_usage(quota_ctx_t qctx)
375 {
376         ext2_filsys fs;
377         ext2_ino_t ino;
378         errcode_t ret;
379         struct ext2_inode inode;
380         qsize_t space;
381         ext2_inode_scan scan;
382
383         if (!qctx)
384                 return 0;
385
386         fs = qctx->fs;
387         ret = ext2fs_open_inode_scan(fs, 0, &scan);
388         if (ret) {
389                 log_err("while opening inode scan. ret=%ld", ret);
390                 return ret;
391         }
392
393         while (1) {
394                 ret = ext2fs_get_next_inode(scan, &ino, &inode);
395                 if (ret) {
396                         log_err("while getting next inode. ret=%ld", ret);
397                         ext2fs_close_inode_scan(scan);
398                         return ret;
399                 }
400                 if (ino == 0)
401                         break;
402                 if (inode.i_links_count) {
403                         space = ext2fs_inode_i_blocks(fs, &inode) << 9;
404                         quota_data_add(qctx, &inode, ino, space);
405                         quota_data_inodes(qctx, &inode, ino, +1);
406                 }
407         }
408
409         ext2fs_close_inode_scan(scan);
410
411         return 0;
412 }
413
414 struct scan_dquots_data {
415         quota_ctx_t         qctx;
416         int                 limit_only; /* read limit only */
417 };
418
419 static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
420 {
421         struct scan_dquots_data *scan_data =
422                 (struct scan_dquots_data *)cb_data;
423         quota_ctx_t qctx = scan_data->qctx;
424         struct dquot *dq;
425
426         dq = get_dq(qctx->quota_dict[dquot->dq_h->qh_type], dquot->dq_id);
427
428         dq->dq_id = dquot->dq_id;
429         if (scan_data->limit_only) {
430                 dq->dq_dqb.u.v2_mdqb.dqb_off = dquot->dq_dqb.u.v2_mdqb.dqb_off;
431                 dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
432                 dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
433                 dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
434                 dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
435         } else {
436                 dq->dq_dqb = dquot->dq_dqb;
437         }
438         return 0;
439 }
440
441 /*
442  * Read all dquots from quota file into memory
443  */
444 static errcode_t quota_read_all_dquots(struct quota_handle *qh,
445                                        quota_ctx_t qctx, int limit_only)
446 {
447         struct scan_dquots_data scan_data;
448
449         scan_data.qctx = qctx;
450         scan_data.limit_only = limit_only;
451
452         return qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data);
453 }
454
455 /*
456  * Write all memory dquots into quota file
457  */
458 static errcode_t quota_write_all_dquots(struct quota_handle *qh,
459                                         quota_ctx_t qctx)
460 {
461         errcode_t err;
462
463         err = ext2fs_read_bitmaps(qctx->fs);
464         if (err)
465                 return err;
466         write_dquots(qctx->quota_dict[qh->qh_type], qh);
467         ext2fs_mark_bb_dirty(qctx->fs);
468         qctx->fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
469         ext2fs_write_bitmaps(qctx->fs);
470         return 0;
471 }
472
473 /*
474  * Update usage of in quota file, limits keep unchaged
475  */
476 errcode_t quota_update_inode(quota_ctx_t qctx, ext2_ino_t qf_ino, int type)
477 {
478         struct quota_handle *qh;
479         errcode_t err;
480
481         if (!qctx)
482                 return 0;
483
484         err = ext2fs_get_mem(sizeof(struct quota_handle), &qh);
485         if (err) {
486                 log_err("Unable to allocate quota handle", "");
487                 return err;
488         }
489
490         err = quota_file_open(qh, qctx->fs, qf_ino, type, -1, EXT2_FILE_WRITE);
491         if (err) {
492                 log_err("Open quota file failed", "");
493                 goto out;
494         }
495
496         quota_read_all_dquots(qh, qctx, 1);
497         quota_write_all_dquots(qh, qctx);
498
499         err = quota_file_close(qh);
500         if (err) {
501                 log_err("Cannot finish IO on new quotafile: %s",
502                         strerror(errno));
503                 if (qh->qh_qf.e2_file)
504                         ext2fs_file_close(qh->qh_qf.e2_file);
505         }
506 out:
507         ext2fs_free_mem(&qh);
508         return err;
509 }