Initial commit to Gerrit
[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 mntent *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         const char *mnt_fsname = NULL;
46
47         if (!hasquota(mnt, type, 0))
48                 goto out_handle;
49         if (!(mnt_fsname = get_device_name(mnt->mnt_fsname)))
50                 goto out_handle;
51         if (stat(mnt_fsname, &h->qh_stat) < 0)
52                 memset(&h->qh_stat, 0, sizeof(struct stat));
53         h->qh_io_flags = 0;
54         if (flags & IOI_READONLY)
55                 h->qh_io_flags |= IOFL_RO;
56         if (flags & IOI_NFS_MIXED_PATHS)
57                 h->qh_io_flags |= IOFL_NFS_MIXED_PATHS;
58         h->qh_type = type;
59         sstrncpy(h->qh_quotadev, mnt_fsname, sizeof(h->qh_quotadev));
60         free((char *)mnt_fsname);
61         sstrncpy(h->qh_fstype, mnt->mnt_type, MAX_FSTYPE_LEN);
62         if (nfs_fstype(mnt->mnt_type)) {        /* NFS filesystem? */
63                 if (fmt != -1 && fmt != QF_RPC) {       /* User wanted some other format? */
64                         errstr(_("Only RPC quota format is allowed on NFS filesystem.\n"));
65                         goto out_handle;
66                 }
67 #ifdef RPC
68                 h->qh_fd = -1;
69                 h->qh_fmt = QF_RPC;
70                 h->qh_ops = &quotafile_ops_rpc;
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->mnt_type, MNTTYPE_XFS)) {      /* XFS filesystem? */
82                 if (fmt != -1 && fmt != QF_XFS) {       /* User wanted some other format? */
83                         errstr(_("Only XFS quota format is allowed on XFS filesystem.\n"));
84                         goto out_handle;
85                 }
86                 h->qh_fd = -1;
87                 h->qh_fmt = QF_XFS;
88                 h->qh_ops = &quotafile_ops_xfs;
89                 memset(&h->qh_info, 0, sizeof(h->qh_info));
90                 h->qh_ops->init_io(h);
91                 return h;
92         }
93         else if (fmt == QF_XFS) {
94                 errstr(_("XFS quota allowed only on XFS filesystem.\n"));
95                 goto out_handle;
96         }
97         if (kern_qfmt_supp(fmt)) {      /* Quota compiled and desired format available? */
98                 /* Quota turned on? */
99                 kernfmt = kern_quota_on(h->qh_quotadev, type, fmt);
100                 if (kernfmt >= 0) {
101                         h->qh_io_flags |= IOFL_QUOTAON;
102                         fmt = kernfmt;  /* Default is kernel used format */
103                 }
104         }
105
106         if (meta_qf_fstype(mnt->mnt_type)) {
107                 if (!QIO_ENABLED(h)) {
108                         errstr(_("Quota not supported by the filesystem.\n"));
109                         goto out_handle;
110                 }
111                 if (flags & IOI_OPENFILE) {
112                         errstr(_("Operation not supported for filesystems with hidden quota files!\n"));
113                         goto out_handle;
114                 }
115                 h->qh_fd = -1;
116                 h->qh_fmt = fmt;
117                 goto set_ops;
118         }
119
120         if (fmt == -1) {
121                 /* Let's try any VFSv0 quota format... */
122                 if (get_qf_name(mnt, type, QF_VFSV0,
123                                 (!QIO_ENABLED(h) || flags & IOI_OPENFILE) ? NF_FORMAT : 0,
124                                 &qfname) >= 0)
125                         fmt = QF_VFSV0;
126                 /* And then VFSv1 quota format... */
127                 else if (get_qf_name(mnt, type, QF_VFSV1,
128                                 (!QIO_ENABLED(h) || flags & IOI_OPENFILE) ? NF_FORMAT : 0,
129                                 &qfname) >= 0)
130                         fmt = QF_VFSV1;
131                 /* And then old quota format... */
132                 else if (get_qf_name(mnt, type, QF_VFSOLD,
133                                 (!QIO_ENABLED(h) || flags & IOI_OPENFILE) ? NF_FORMAT : 0,
134                                 &qfname) >= 0)
135                         fmt = QF_VFSOLD;
136                 else {  /* Don't know... */
137                         errstr(_("Cannot find any quota file to work on.\n"));
138                         goto out_handle;
139                 }
140         } else {
141                 if (get_qf_name(mnt, type, fmt,
142                                 (!QIO_ENABLED(h) || flags & IOI_OPENFILE) ? NF_FORMAT : 0,
143                                 &qfname) < 0) {
144                         errstr(_("Quota file not found or has wrong format.\n"));
145                         goto out_handle;
146                 }
147         }
148         if (!QIO_ENABLED(h) || flags & IOI_OPENFILE) {  /* Need to open file? */
149                 /* We still need to open file for operations like 'repquota' */
150                 if ((fd = open(qfname, QIO_RO(h) ? O_RDONLY : O_RDWR)) < 0) {
151                         errstr(_("Cannot open quotafile %s: %s\n"),
152                                 qfname, strerror(errno));
153                         goto out_handle;
154                 }
155                 flock(fd, QIO_RO(h) ? LOCK_SH : LOCK_EX);
156                 /* Init handle */
157                 h->qh_fd = fd;
158                 h->qh_fmt = fmt;
159         } else {
160                 h->qh_fd = -1;
161                 h->qh_fmt = fmt;
162         }
163         free(qfname);   /* We don't need it anymore */
164         qfname = NULL;
165
166 set_ops:
167         if (fmt == QF_VFSOLD)
168                 h->qh_ops = &quotafile_ops_1;
169         else if (is_tree_qfmt(fmt))
170                 h->qh_ops = &quotafile_ops_2;
171         else if (fmt == QF_META)
172                 h->qh_ops = &quotafile_ops_meta;
173         memset(&h->qh_info, 0, sizeof(h->qh_info));
174
175         if (h->qh_ops->init_io && h->qh_ops->init_io(h) < 0) {
176                 errstr(_("Cannot initialize quota on %s: %s\n"), h->qh_quotadev, strerror(errno));
177                 goto out_lock;
178         }
179         return h;
180 out_lock:
181         if (fd != -1)
182                 flock(fd, LOCK_UN);
183 out_handle:
184         if (qfname)
185                 free(qfname);
186         free(h);
187         return NULL;
188 }
189
190 /*
191  *      Create new quotafile of specified format on given filesystem
192  */
193 struct quota_handle *new_io(struct mntent *mnt, int type, int fmt)
194 {
195         char *qfname;
196         int fd;
197         struct quota_handle *h;
198         const char *mnt_fsname;
199         char namebuf[PATH_MAX];
200
201         if (fmt == -1)
202                 fmt = QF_VFSV0;
203         else if (fmt == QF_RPC || fmt == QF_XFS || meta_qf_fstype(mnt->mnt_type)) {
204                 errstr(_("Creation of %s quota format is not supported.\n"),
205                         fmt2name(fmt));
206                 return NULL;
207         }
208         if (get_qf_name(mnt, type, fmt, 0, &qfname) < 0)
209                 return NULL;
210         sstrncpy(namebuf, qfname, PATH_MAX);
211         sstrncat(namebuf, ".new", PATH_MAX);
212         free(qfname);
213         if ((fd = open(namebuf, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0) {
214                 errstr(_("Cannot create new quotafile %s: %s\n"),
215                         namebuf, strerror(errno));
216                 return NULL;
217         }
218         if (!(mnt_fsname = get_device_name(mnt->mnt_fsname)))
219                 goto out_fd;
220         h = smalloc(sizeof(struct quota_handle));
221
222         h->qh_fd = fd;
223         h->qh_io_flags = 0;
224         sstrncpy(h->qh_quotadev, mnt_fsname, sizeof(h->qh_quotadev));
225         free((char *)mnt_fsname);
226         h->qh_type = type;
227         h->qh_fmt = fmt;
228         memset(&h->qh_info, 0, sizeof(h->qh_info));
229         if (fmt == QF_VFSOLD)
230                 h->qh_ops = &quotafile_ops_1;
231         else
232                 h->qh_ops = &quotafile_ops_2;
233
234         flock(fd, LOCK_EX);
235         if (h->qh_ops->new_io && h->qh_ops->new_io(h) < 0) {
236                 flock(fd, LOCK_UN);
237                 free(h);
238                 goto out_fd;
239         }
240         return h;
241 out_fd:
242         close(fd);
243         return NULL;
244 }
245
246 /*
247  *      Close quotafile and release handle
248  */
249 int end_io(struct quota_handle *h)
250 {
251         if (h->qh_io_flags & IOFL_INFODIRTY) {
252                 if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
253                         return -1;
254                 h->qh_io_flags &= ~IOFL_INFODIRTY;
255         }
256         if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
257                 return -1;
258         if (h->qh_fd != -1) {
259                 flock(h->qh_fd, LOCK_UN);
260                 close(h->qh_fd);
261         }
262         free(h);
263         return 0;
264 }
265
266 /*
267  *      Create empty quota structure
268  */
269 struct dquot *get_empty_dquot(void)
270 {
271         struct dquot *dquot = smalloc(sizeof(struct dquot));
272
273         memset(dquot, 0, sizeof(*dquot));
274         dquot->dq_id = -1;
275         return dquot;
276 }