Initial commit to Gerrit
[profile/ivi/quota.git] / convertquota.c
1 /*
2  *
3  *      Utility for converting quota file from old to new format
4  *
5  *      Sponsored by SuSE CR
6  */
7
8 #include "config.h"
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <getopt.h>
17
18 #include <asm/byteorder.h>
19
20 #include "pot.h"
21 #include "common.h"
22 #include "quotaio.h"
23 #include "quotasys.h"
24 #include "quota.h"
25 #include "bylabel.h"
26 #include "quotaio_v2.h"
27 #include "dqblk_v2.h"
28
29 #define ACT_FORMAT 1            /* Convert format from old to new */
30 #define ACT_ENDIAN 2            /* Convert endianity */
31
32 char *mntpoint;
33 char *progname;
34 int ucv, gcv;
35 struct quota_handle *qn;        /* Handle of new file */
36 int action;                     /* Action to be performed */
37 int infmt, outfmt;
38
39 static void usage(void)
40 {
41         errstr(_("Utility for converting quota files.\nUsage:\n\t%s [options] mountpoint\n\n\
42 -u, --user                          convert user quota file\n\
43 -g, --group                         convert group quota file\n\
44 -e, --convert-endian                convert quota file to correct endianity\n\
45 -f, --convert-format oldfmt,newfmt  convert from old to VFSv0 quota format\n\
46 -h, --help                          show this help text and exit\n\
47 -V, --version                       output version information and exit\n\n"), progname);
48         errstr(_("Bugs to %s\n"), MY_EMAIL);
49         exit(1);
50 }
51
52 static inline unsigned int min(unsigned a, unsigned b)
53 {
54         if (a < b)
55                 return a;
56         return b;
57 }
58
59 #define MAX_FMTNAME_LEN 32
60
61 static void parse_options(int argcnt, char **argstr)
62 {
63         int ret;
64         struct option long_opts[] = {
65                 { "help", 0, NULL, 'h'},
66                 { "version", 0, NULL, 'V'},
67                 { "user", 0, NULL, 'u'},
68                 { "group", 0, NULL, 'g'},
69                 { "convert-endian", 0, NULL, 'e'},
70                 { "convert-format", 1, NULL, 'f'},
71                 { NULL, 0, NULL, 0}
72         };
73         char *comma;
74         char fmtbuf[MAX_FMTNAME_LEN];
75
76         while ((ret = getopt_long(argcnt, argstr, "Vugef:h", long_opts, NULL)) != -1) {
77                 switch (ret) {
78                         case '?':
79                         case 'h':
80                                 usage();
81                         case 'V':
82                                 version();
83                                 exit(0);
84                         case 'u':
85                                 ucv = 1;
86                                 break;
87                         case 'g':
88                                 gcv = 1;
89                                 break;
90                         case 'e':
91                                 action = ACT_ENDIAN;
92                                 break;
93                         case 'f':
94                                 action = ACT_FORMAT;
95                                 comma = strchr(optarg, ',');
96                                 if (!comma) {
97                                         errstr(_("You have to specify source and target format of conversion.\n"));
98                                         usage();
99                                 }
100                                 sstrncpy(fmtbuf, optarg, min(comma - optarg + 1, MAX_FMTNAME_LEN));
101                                 infmt = name2fmt(fmtbuf);
102                                 if (infmt == QF_ERROR)
103                                         usage();
104                                 outfmt = name2fmt(comma + 1);
105                                 if (outfmt == QF_ERROR)
106                                         usage();
107                                 break;
108                 }
109         }
110
111         if (optind + 1 != argcnt) {
112                 errstr(_("Bad number of arguments.\n"));
113                 usage();
114         }
115
116         if (!(ucv | gcv))
117                 ucv = 1;
118         if (!action) {
119                 errstr(_("You have to specify action to perform.\n"));
120                 usage();
121         }
122
123         mntpoint = argstr[optind];
124 }
125
126 /*
127  *      Implementation of endian conversion
128  */
129
130 typedef char *dqbuf_t;
131
132 #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
133 #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
134
135 #define getdqbuf() smalloc(QT_BLKSIZE)
136 #define freedqbuf(buf) free(buf)
137
138 static inline void endian_disk2memdqblk(struct util_dqblk *m, struct v2r0_disk_dqblk *d)
139 {
140         m->dqb_ihardlimit = __be32_to_cpu(d->dqb_ihardlimit);
141         m->dqb_isoftlimit = __be32_to_cpu(d->dqb_isoftlimit);
142         m->dqb_bhardlimit = __be32_to_cpu(d->dqb_bhardlimit);
143         m->dqb_bsoftlimit = __be32_to_cpu(d->dqb_bsoftlimit);
144         m->dqb_curinodes = __be32_to_cpu(d->dqb_curinodes);
145         m->dqb_curspace = __be64_to_cpu(d->dqb_curspace);
146         m->dqb_itime = __be64_to_cpu(d->dqb_itime);
147         m->dqb_btime = __be64_to_cpu(d->dqb_btime);
148 }
149
150 /* Is given dquot empty? */
151 static int endian_empty_dquot(struct v2r0_disk_dqblk *d)
152 {
153         static struct v2r0_disk_dqblk fakedquot;
154
155         return !memcmp(d, &fakedquot, sizeof(fakedquot));
156 }
157
158 /* Read given block */
159 static void read_blk(int fd, uint blk, dqbuf_t buf)
160 {
161         int err;
162
163         lseek(fd, blk << QT_BLKSIZE_BITS, SEEK_SET);
164         err = read(fd, buf, QT_BLKSIZE);
165         if (err < 0)
166                 die(2, _("Cannot read block %u: %s\n"), blk, strerror(errno));
167         else if (err != QT_BLKSIZE)
168                 memset(buf + err, 0, QT_BLKSIZE - err);
169 }
170
171 static void endian_report_block(int fd, uint blk, char *bitmap)
172 {
173         dqbuf_t buf = getdqbuf();
174         struct qt_disk_dqdbheader *dh;
175         struct v2r0_disk_dqblk *ddata;
176         struct dquot dquot;
177         struct qtree_mem_dqinfo *info = &qn->qh_info.u.v2_mdqi.dqi_qtree;
178         int i;
179
180         set_bit(bitmap, blk);
181         read_blk(fd, blk, buf);
182         dh = (struct qt_disk_dqdbheader *)buf;
183         ddata = (struct v2r0_disk_dqblk *)(dh + 1);
184         for (i = 0; i < qtree_dqstr_in_blk(info); i++)
185                 if (!endian_empty_dquot(ddata + i)) {
186                         memset(&dquot, 0, sizeof(dquot));
187                         dquot.dq_h = qn;
188                         endian_disk2memdqblk(&dquot.dq_dqb, ddata + i);
189                         dquot.dq_id = __be32_to_cpu(ddata[i].dqb_id);
190                         if (qn->qh_ops->commit_dquot(&dquot, COMMIT_ALL) < 0)
191                                 errstr(_("Cannot commit dquot for id %u: %s\n"),
192                                         (uint)dquot.dq_id, strerror(errno));
193                 }
194         freedqbuf(buf);
195 }
196
197 static void endian_report_tree(int fd, uint blk, int depth, char *bitmap)
198 {
199         int i;
200         dqbuf_t buf = getdqbuf();
201         u_int32_t *ref = (u_int32_t *) buf;
202
203         read_blk(fd, blk, buf);
204         if (depth == QT_TREEDEPTH - 1) {
205                 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
206                         blk = __be32_to_cpu(ref[i]);
207                         if (blk && !get_bit(bitmap, blk))
208                                 endian_report_block(fd, blk, bitmap);
209                 }
210         }
211         else {
212                 for (i = 0; i < QT_BLKSIZE >> 2; i++)
213                         if ((blk = __be32_to_cpu(ref[i])))
214                                 endian_report_tree(fd, blk, depth + 1, bitmap);
215         }
216         freedqbuf(buf);
217 }
218
219 static int endian_scan_structures(int fd, int type)
220 {
221         char *bitmap;
222         loff_t blocks = (lseek(fd, 0, SEEK_END) + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS;
223
224         bitmap = smalloc((blocks + 7) >> 3);
225         memset(bitmap, 0, (blocks + 7) >> 3);
226         endian_report_tree(fd, QT_TREEOFF, 0, bitmap);
227         free(bitmap);
228         return 0;
229 }
230
231 static int endian_check_header(int fd, int type)
232 {
233         struct v2_disk_dqheader head;
234         u_int32_t file_magics[] = INITQMAGICS;
235         u_int32_t known_versions[] = INIT_V2_VERSIONS;
236
237         lseek(fd, 0, SEEK_SET);
238         if (read(fd, &head, sizeof(head)) != sizeof(head)) {
239                 errstr(_("Cannot read header of old quotafile.\n"));
240                 return -1;
241         }
242         if (__be32_to_cpu(head.dqh_magic) != file_magics[type] || __be32_to_cpu(head.dqh_version) > known_versions[type]) {
243                 errstr(_("Bad file magic or version (probably not quotafile with bad endianity).\n"));
244                 return -1;
245         }
246         return 0;
247 }
248
249 static int endian_load_info(int fd, int type)
250 {
251         struct v2_disk_dqinfo dinfo;
252
253         if (read(fd, &dinfo, sizeof(dinfo)) != sizeof(dinfo)) {
254                 errstr(_("Cannot read information about old quotafile.\n"));
255                 return -1;
256         }
257         qn->qh_info.u.v2_mdqi.dqi_flags = __be32_to_cpu(dinfo.dqi_flags);
258         qn->qh_info.dqi_bgrace = __be32_to_cpu(dinfo.dqi_bgrace);
259         qn->qh_info.dqi_igrace = __be32_to_cpu(dinfo.dqi_igrace);
260         return 0;
261 }
262
263 /*
264  *      End of endian conversion
265  */
266
267 static int convert_dquot(struct dquot *dquot, char *name)
268 {
269         struct dquot newdquot;
270
271         memset(&newdquot, 0, sizeof(newdquot));
272         newdquot.dq_id = dquot->dq_id;
273         newdquot.dq_h = qn;
274         newdquot.dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
275         newdquot.dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
276         newdquot.dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
277         newdquot.dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
278         newdquot.dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
279         newdquot.dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
280         newdquot.dq_dqb.dqb_btime = dquot->dq_dqb.dqb_btime;
281         newdquot.dq_dqb.dqb_itime = dquot->dq_dqb.dqb_itime;
282         if (qn->qh_ops->commit_dquot(&newdquot, COMMIT_ALL) < 0) {
283                 errstr(_("Cannot commit dquot for id %u: %s\n"),
284                         (uint)dquot->dq_id, strerror(errno));
285                 return -1;
286         }
287         return 0;
288 }
289
290 static int rename_file(int type, int fmt, struct mntent *mnt)
291 {
292         char *qfname, namebuf[PATH_MAX];
293         int ret = 0;
294
295         if (get_qf_name(mnt, type, fmt, 0, &qfname) < 0) {
296                 errstr(_("Cannot get name of new quotafile.\n"));
297                 return -1;
298         }
299         strcpy(namebuf, qfname);
300         sstrncat(namebuf, ".new", sizeof(namebuf));
301         if (rename(namebuf, qfname) < 0) {
302                 errstr(_("Cannot rename new quotafile %s to name %s: %s\n"),
303                         namebuf, qfname, strerror(errno));
304                 ret = -1;
305         }
306         free(qfname);
307         return ret;
308 }
309
310 static int convert_format(int type, struct mntent *mnt)
311 {
312         struct quota_handle *qo;
313         int ret = 0;
314         
315         if (!(qo = init_io(mnt, type, infmt, IOI_OPENFILE))) {
316                 errstr(_("Cannot open old format file for %ss on %s\n"),
317                         type2name(type), mnt->mnt_dir);
318                 return -1;
319         }
320         if (!(qn = new_io(mnt, type, outfmt))) {
321                 errstr(_("Cannot create file for %ss for new format on %s: %s\n"),
322                         type2name(type), mnt->mnt_dir, strerror(errno));
323                 end_io(qo);
324                 return -1;
325         }
326         if (qo->qh_ops->scan_dquots(qo, convert_dquot) >= 0)    /* Conversion succeeded? */
327                 ret = rename_file(type, outfmt, mnt);
328         else
329                 ret = -1;
330         end_io(qo);
331         end_io(qn);
332         return ret;
333 }
334
335 static int convert_endian(int type, struct mntent *mnt)
336 {
337         int ret = 0;
338         int ofd;
339         char *qfname;
340
341         if (get_qf_name(mnt, type, QF_VFSV0, NF_EXIST, &qfname) < 0)
342                 return -1;
343         if ((ofd = open(qfname, O_RDONLY)) < 0) {
344                 errstr(_("Cannot open old quota file on %s: %s\n"), mnt->mnt_dir, strerror(errno));
345                 free(qfname);
346                 return -1;
347         }
348         free(qfname);
349         if (endian_check_header(ofd, type) < 0) {
350                 close(ofd);
351                 return -1;
352         }
353         if (!(qn = new_io(mnt, type, QF_VFSV0))) {
354                 errstr(_("Cannot create file for %ss for new format on %s: %s\n"),
355                         type2name(type), mnt->mnt_dir, strerror(errno));
356                 close(ofd);
357                 return -1;
358         }
359         if (endian_load_info(ofd, type) < 0) {
360                 end_io(qn);
361                 close(ofd);
362                 return -1;
363         }
364         ret = endian_scan_structures(ofd, type);
365         end_io(qn);
366         if (ret < 0)
367                 return ret;
368         
369         return rename_file(type, QF_VFSV0, mnt);
370 }
371
372 static int convert_file(int type, struct mntent *mnt)
373 {
374         switch (action) {
375                 case ACT_FORMAT:
376                         return convert_format(type, mnt);
377                 case ACT_ENDIAN:
378                         return convert_endian(type, mnt);
379         }
380         errstr(_("Unknown action should be performed.\n"));
381         return -1;
382 }
383
384 int main(int argc, char **argv)
385 {
386         struct mntent *mnt;
387         int ret = 0;
388         
389         gettexton();
390         progname = basename(argv[0]);
391
392         parse_options(argc, argv);
393         init_kernel_interface();
394         if (init_mounts_scan(1, &mntpoint, 0) < 0)
395                 return 1;
396         if (!(mnt = get_next_mount())) {
397                 end_mounts_scan();
398                 return 1;
399         }
400         if (ucv)
401                 ret |= convert_file(USRQUOTA, mnt);
402         if (gcv)
403                 ret |= convert_file(GRPQUOTA, mnt);
404         end_mounts_scan();
405
406         if (ret)
407                 return 1;
408         return 0;
409 }