Update to latest version of quota.
[profile/ivi/quota.git] / quotaio.c
1 /*
2  *
3  *      Generic IO operations on quotafiles
4  *
5  *      Jan Kara <jack@suse.cz> - sponsored by SuSE CR
6  */
7
8 #include "config.h"
9
10 #include <stdio.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/file.h>
18 #include <asm/byteorder.h>
19
20 #include "pot.h"
21 #include "bylabel.h"
22 #include "common.h"
23 #include "quotasys.h"
24 #include "quotaio.h"
25
26 #include "dqblk_v1.h"
27 #include "dqblk_v2.h"
28 #include "dqblk_rpc.h"
29 #include "dqblk_xfs.h"
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  *      Detect quota format and initialize quota IO
39  */
40 struct quota_handle *init_io(struct mount_entry *mnt, int type, int fmt, int flags)
41 {
42         char *qfname = NULL;
43         int fd = -1, kernfmt;
44         struct quota_handle *h = smalloc(sizeof(struct quota_handle));
45         int nameflag;
46
47         if (!me_hasquota(mnt, type))
48                 goto out_handle;
49         if (stat(mnt->me_devname, &h->qh_stat) < 0)
50                 memset(&h->qh_stat, 0, sizeof(struct stat));
51         h->qh_io_flags = 0;
52         if (flags & IOI_READONLY)
53                 h->qh_io_flags |= IOFL_RO;
54         if (flags & IOI_NFS_MIXED_PATHS)
55                 h->qh_io_flags |= IOFL_NFS_MIXED_PATHS;
56         h->qh_type = type;
57         sstrncpy(h->qh_quotadev, mnt->me_devname, sizeof(h->qh_quotadev));
58         sstrncpy(h->qh_fstype, mnt->me_type, MAX_FSTYPE_LEN);
59         sstrncpy(h->qh_dir, mnt->me_dir, PATH_MAX);
60         if (nfs_fstype(mnt->me_type)) { /* NFS filesystem? */
61                 if (fmt != -1 && fmt != QF_RPC) {       /* User wanted some other format? */
62                         errstr(_("Only RPC quota format is allowed on NFS filesystem.\n"));
63                         goto out_handle;
64                 }
65 #ifdef RPC
66                 h->qh_fd = -1;
67                 h->qh_fmt = QF_RPC;
68                 h->qh_ops = &quotafile_ops_rpc;
69                 memset(&h->qh_info, 0, sizeof(h->qh_info));
70                 h->qh_ops->init_io(h);
71                 return h;
72 #else
73                 errstr(_("RPC quota format not compiled.\n"));
74                 goto out_handle;
75 #endif
76         } else if (fmt == QF_RPC) {
77                 errstr(_("RPC quota format specified for non-NFS filesystem.\n"));
78                 goto out_handle;
79         }
80
81         if (!strcmp(mnt->me_type, MNTTYPE_XFS) ||       /* XFS filesystem? */
82             !strcmp(mnt->me_type, MNTTYPE_GFS2)) {      /* XFS filesystem? */
83                 if (fmt != -1 && fmt != QF_XFS) {       /* User wanted some other format? */
84                         errstr(_("Only XFS quota format is allowed on XFS filesystem.\n"));
85                         goto out_handle;
86                 }
87                 h->qh_fd = -1;
88                 h->qh_fmt = QF_XFS;
89                 h->qh_ops = &quotafile_ops_xfs;
90                 memset(&h->qh_info, 0, sizeof(h->qh_info));
91                 h->qh_ops->init_io(h);
92                 return h;
93         }
94         else if (fmt == QF_XFS) {
95                 errstr(_("XFS quota allowed only on XFS filesystem.\n"));
96                 goto out_handle;
97         }
98         if (kern_qfmt_supp(fmt)) {      /* Quota compiled and desired format available? */
99                 /* Quota turned on? */
100                 kernfmt = kern_quota_on(mnt, type, fmt);
101                 if (kernfmt >= 0) {
102                         h->qh_io_flags |= IOFL_QUOTAON;
103                         fmt = kernfmt;  /* Default is kernel used format */
104                 }
105         }
106
107         if (meta_qf_fstype(mnt->me_type) || mnt->me_qfmt[type] == QF_META) {
108                 if (!QIO_ENABLED(h)) {
109                         errstr(_("Quota not supported by the filesystem.\n"));
110                         goto out_handle;
111                 }
112                 h->qh_fd = -1;
113                 h->qh_fmt = fmt;
114                 goto set_ops;
115         }
116
117         nameflag = (!QIO_ENABLED(h) || flags & IOI_INITSCAN) ? NF_FORMAT : 0;
118         if (fmt == -1) {
119                 /* Let's try any VFSv0 quota format... */
120                 if (get_qf_name(mnt, type, QF_VFSV0, nameflag, &qfname) >= 0)
121                         fmt = QF_VFSV0;
122                 /* And then VFSv1 quota format... */
123                 else if (get_qf_name(mnt, type, QF_VFSV1, nameflag, &qfname) >= 0)
124                         fmt = QF_VFSV1;
125                 /* And then old quota format... */
126                 else if (get_qf_name(mnt, type, QF_VFSOLD, nameflag, &qfname) >= 0)
127                         fmt = QF_VFSOLD;
128                 else {  /* Don't know... */
129                         errstr(_("Cannot find any quota file to work on.\n"));
130                         goto out_handle;
131                 }
132         } else {
133                 if (get_qf_name(mnt, type, fmt, nameflag, &qfname) < 0) {
134                         errstr(_("Quota file not found or has wrong format.\n"));
135                         goto out_handle;
136                 }
137         }
138         if (!QIO_ENABLED(h) || flags & IOI_INITSCAN) {  /* Need to open file? */
139                 if (QIO_ENABLED(h)) {   /* Kernel uses same file? */
140                         unsigned int cmd =
141                                 (kernel_iface == IFACE_GENERIC) ? Q_SYNC : Q_6_5_SYNC;
142                         if (quotactl(QCMD(cmd, h->qh_type), h->qh_quotadev,
143                                      0, NULL) < 0) {
144                                 die(4, _("Cannot sync quotas on device %s: %s\n"),
145                                     h->qh_quotadev, strerror(errno));
146                         }
147                 }
148                 /* We still need to open file for operations like 'repquota' */
149                 if ((fd = open(qfname, QIO_RO(h) ? O_RDONLY : O_RDWR)) < 0) {
150                         errstr(_("Cannot open quotafile %s: %s\n"),
151                                 qfname, strerror(errno));
152                         goto out_handle;
153                 }
154                 flock(fd, QIO_RO(h) ? LOCK_SH : LOCK_EX);
155                 /* Init handle */
156                 h->qh_fd = fd;
157                 h->qh_fmt = fmt;
158         } else {
159                 h->qh_fd = -1;
160                 h->qh_fmt = fmt;
161         }
162         free(qfname);   /* We don't need it anymore */
163         qfname = NULL;
164
165 set_ops:
166         if (fmt == QF_VFSOLD)
167                 h->qh_ops = &quotafile_ops_1;
168         else if (is_tree_qfmt(fmt))
169                 h->qh_ops = &quotafile_ops_2;
170         else if (fmt == QF_META)
171                 h->qh_ops = &quotafile_ops_meta;
172         memset(&h->qh_info, 0, sizeof(h->qh_info));
173
174         if (h->qh_ops->init_io && h->qh_ops->init_io(h) < 0) {
175                 errstr(_("Cannot initialize quota on %s: %s\n"), h->qh_quotadev, strerror(errno));
176                 goto out_lock;
177         }
178         return h;
179 out_lock:
180         if (fd != -1)
181                 flock(fd, LOCK_UN);
182 out_handle:
183         if (qfname)
184                 free(qfname);
185         free(h);
186         return NULL;
187 }
188
189 /*
190  *      Create new quotafile of specified format on given filesystem
191  */
192 struct quota_handle *new_io(struct mount_entry *mnt, int type, int fmt)
193 {
194         char *qfname;
195         int fd;
196         struct quota_handle *h;
197         char namebuf[PATH_MAX];
198
199         if (fmt == -1)
200                 fmt = QF_VFSV0;
201         else if (fmt == QF_RPC || fmt == QF_XFS) {
202                 errstr(_("Creation of %s quota format is not supported.\n"),
203                         fmt2name(fmt));
204                 return NULL;
205         }
206         /*
207          * For filesystems which never have quotas in quota files or for
208          * filesystems which have quotas already stored in system files we
209          * refuse to create anything.
210          */
211         if (meta_qf_fstype(mnt->me_type) || mnt->me_qfmt[type] == QF_META) {
212                 errstr(_("Quota on %s is stored in system files and must"
213                          " be manipulated by fs tools.\n"), mnt->me_dir);
214                 return NULL;
215         }
216         if (get_qf_name(mnt, type, fmt, 0, &qfname) < 0)
217                 return NULL;
218         sstrncpy(namebuf, qfname, PATH_MAX);
219         sstrncat(namebuf, ".new", PATH_MAX);
220         free(qfname);
221         if ((fd = open(namebuf, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0) {
222                 errstr(_("Cannot create new quotafile %s: %s\n"),
223                         namebuf, strerror(errno));
224                 return NULL;
225         }
226         h = smalloc(sizeof(struct quota_handle));
227
228         h->qh_fd = fd;
229         h->qh_io_flags = 0;
230         sstrncpy(h->qh_quotadev, mnt->me_devname, sizeof(h->qh_quotadev));
231         sstrncpy(h->qh_fstype, mnt->me_type, MAX_FSTYPE_LEN);
232         sstrncpy(h->qh_dir, mnt->me_dir, PATH_MAX);
233         h->qh_type = type;
234         h->qh_fmt = fmt;
235         memset(&h->qh_info, 0, sizeof(h->qh_info));
236         if (fmt == QF_VFSOLD)
237                 h->qh_ops = &quotafile_ops_1;
238         else
239                 h->qh_ops = &quotafile_ops_2;
240
241         flock(fd, LOCK_EX);
242         if (h->qh_ops->new_io && h->qh_ops->new_io(h) < 0) {
243                 flock(fd, LOCK_UN);
244                 free(h);
245                 goto out_fd;
246         }
247         return h;
248 out_fd:
249         close(fd);
250         return NULL;
251 }
252
253 /*
254  *      Close quotafile and release handle
255  */
256 int end_io(struct quota_handle *h)
257 {
258         if (h->qh_io_flags & IOFL_INFODIRTY) {
259                 if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
260                         return -1;
261                 h->qh_io_flags &= ~IOFL_INFODIRTY;
262         }
263         if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
264                 return -1;
265         if (h->qh_fd != -1) {
266                 flock(h->qh_fd, LOCK_UN);
267                 close(h->qh_fd);
268         }
269         free(h);
270         return 0;
271 }
272
273 /*
274  *      Create empty quota structure
275  */
276 struct dquot *get_empty_dquot(void)
277 {
278         struct dquot *dquot = smalloc(sizeof(struct dquot));
279
280         memset(dquot, 0, sizeof(*dquot));
281         dquot->dq_id = -1;
282         return dquot;
283 }
284
285 /*
286  *      Check whether values in current dquot can be stored on disk
287  */
288 int check_dquot_range(struct dquot *dquot)
289 {
290         struct util_dqinfo *info = &dquot->dq_h->qh_info;
291
292         if (dquot->dq_dqb.dqb_bhardlimit > info->dqi_max_b_limit ||
293             dquot->dq_dqb.dqb_bsoftlimit > info->dqi_max_b_limit ||
294             dquot->dq_dqb.dqb_ihardlimit > info->dqi_max_i_limit ||
295             dquot->dq_dqb.dqb_isoftlimit > info->dqi_max_i_limit) {
296                 errstr(_("Trying to set quota limits out of range "
297                                  "supported by quota format on %s.\n"), dquot->dq_h->qh_quotadev);
298                 return -1;
299         }
300         if (dquot->dq_dqb.dqb_curinodes > info->dqi_max_i_usage ||
301             dquot->dq_dqb.dqb_curspace > info->dqi_max_b_usage) {
302                 errstr(_("Trying to set quota usage out of range "
303                          "supported by quota format on %s.\n"), dquot->dq_h->qh_quotadev);
304                 return -1;
305         }
306         return 0;
307 }