Whamcloud - gitweb
b=21147 be tolerant to setting the same type and version of quota
[fs/lustre-release.git] / lustre / lvfs / lustre_quota_fmt_convert.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/lvfs/lustre_quota_fmt_convert.c
37  *
38  * convert quota format.
39  * from linux/fs/quota_v2.c
40  */
41
42 #ifndef EXPORT_SYMTAB
43 # define EXPORT_SYMTAB
44 #endif
45
46 #include <linux/errno.h>
47 #include <linux/fs.h>
48 #include <linux/mount.h>
49 #include <linux/kernel.h>
50 #include <linux/init.h>
51 #include <linux/module.h>
52 #include <linux/slab.h>
53 #ifdef HAVE_QUOTAIO_V1_H
54 # include <linux/quotaio_v1.h>
55 #endif
56
57 #include <asm/byteorder.h>
58 #include <asm/uaccess.h>
59
60 #include <lustre_quota.h>
61 #include <obd_support.h>
62 #include "lustre_quota_fmt.h"
63
64 #ifdef HAVE_QUOTA_SUPPORT
65
66 static int admin_convert_dqinfo(struct file *fp_v1, struct file *fp_v2,
67                                 struct lustre_quota_info *lqi, int type)
68 {
69         struct lustre_mem_dqinfo *info_old, *info_new = &lqi->qi_info[type];
70         int rc;
71
72         OBD_ALLOC_PTR(info_old);
73         if (info_old == NULL)
74                 return -ENOMEM;
75
76         rc = lustre_read_quota_file_info(fp_v1, info_old);
77         if (!rc) {
78                 /* save essential fields: bgrace, igrace, flags */
79                 info_new->dqi_bgrace = info_old->dqi_bgrace;
80                 info_new->dqi_igrace = info_old->dqi_igrace;
81                 info_new->dqi_flags  = info_old->dqi_flags;
82                 rc = lustre_write_quota_info(lqi, type);
83         }
84
85         OBD_FREE_PTR(info_old);
86
87         return rc;
88 }
89
90 static int quota_convert_v1_to_v2(struct file *fp_v1, struct file *fp_v2,
91                                   struct lustre_quota_info *lqi, int type)
92 {
93         struct list_head blk_list;
94         struct dqblk *blk_item, *tmp;
95         dqbuf_t buf = NULL;
96         struct lustre_disk_dqblk *ddquot;
97         struct lustre_dquot *dquot = NULL;
98         int rc;
99
100         ENTRY;
101
102         INIT_LIST_HEAD(&blk_list);
103
104         rc = admin_convert_dqinfo(fp_v1, fp_v2, lqi, type);
105         if (rc) {
106                 CERROR("could not copy dqinfo!(%d)\n", rc);
107                 GOTO(out_free, rc);
108         }
109
110         rc = walk_tree_dqentry(fp_v1, NULL, type, LUSTRE_DQTREEOFF, 0, &blk_list);
111         if (rc) {
112                 CERROR("walk through quota file failed!(%d)\n", rc);
113                 GOTO(out_free, rc);
114         }
115         if (list_empty(&blk_list))
116                 RETURN(0);
117
118         buf = getdqbuf();
119         if (!buf)
120                 GOTO(out_free, rc = -ENOMEM);
121
122         ddquot = (struct lustre_disk_dqblk*)GETENTRIES(buf, LUSTRE_QUOTA_V1);
123
124         OBD_ALLOC_PTR(dquot);
125         if (dquot == NULL)
126                 GOTO(out_free, rc = -ENOMEM);
127
128         list_for_each_entry(blk_item, &blk_list, link) {
129                 loff_t ret = 0;
130                 int i;
131                 struct lustre_disk_dqblk fakedquot;
132
133                 memset(buf, 0, LUSTRE_DQBLKSIZE);
134
135                 if ((ret = lustre_read_quota(fp_v1, NULL, type, buf, LUSTRE_DQBLKSIZE,
136                                  blk_item->blk << LUSTRE_DQBLKSIZE_BITS)) < 0) {
137                         CERROR("VFS: Can't read quota tree block %u.\n",
138                                blk_item->blk);
139                         GOTO(out_free, rc = ret);
140                 }
141
142                 memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk));
143                 for (i = 0; i < LUSTRE_DQSTRINBLK; i++) {
144                         /* skip empty entry */
145                         if (!memcmp
146                             (&fakedquot, ddquot + i,
147                              sizeof(struct lustre_disk_dqblk)))
148                                 continue;
149
150                         memset(dquot, 0, sizeof(*dquot));
151
152                         dquot->dq_id = le32_to_cpu(ddquot[i].dqb_id);
153                         dquot->dq_type = type;
154                         dquot->dq_info = lqi;
155
156                         disk2memdqb(&dquot->dq_dqb, &ddquot[i], LUSTRE_QUOTA_V1);
157                         rc = lustre_commit_dquot(dquot);
158                         if (rc < 0)
159                                 GOTO(out_free, rc);
160                 }
161         }
162
163         EXIT;
164
165 out_free:
166         list_for_each_entry_safe(blk_item, tmp, &blk_list, link) {
167                 list_del_init(&blk_item->link);
168                 kfree(blk_item);
169         }
170         if (buf)
171                 freedqbuf(buf);
172         if (dquot)
173                 OBD_FREE_PTR(dquot);
174         return rc;
175 }
176
177 int lustre_quota_convert(struct lustre_quota_info *lqi, int type)
178 {
179         struct file *f_v2 = lqi->qi_files[type];
180         const char *qf_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1;
181         char name[64];
182         struct file *f_v1;
183         int rc = 0;
184         ENTRY;
185
186         LASSERT(f_v2);
187
188         rc = lustre_init_quota_info_generic(lqi, type, 1);
189         if (rc) {
190                 CERROR("could not initialize new quota file(%d)\n", rc);
191                 RETURN(rc);
192         }
193
194         /* Open old quota file and copy to the new one */
195         sprintf(name, "OBJECTS/%s", qf_v1[type]);
196         f_v1 = filp_open(name, O_RDONLY, 0);
197         if (!IS_ERR(f_v1)) {
198                 if (!check_quota_file(f_v1, NULL, type, LUSTRE_QUOTA_V1)) {
199                         rc = quota_convert_v1_to_v2(f_v1, f_v2, lqi, type);
200                         if (rc)
201                                 CERROR("failed to convert v1 quota file"
202                                        " to v2 quota file.\n");
203                         else
204                                 CDEBUG(D_INFO, "Found v1 quota file, "
205                                                "successfully converted to v2.\n");
206                 }
207                 else
208                         CERROR("old quota file is broken, "
209                                "new quota file will be empty\n");
210
211                 filp_close(f_v1, 0);
212         } else if (PTR_ERR(f_v1) != -ENOENT) /* No quota file is ok */
213                 CERROR("old quota file can not be open, "
214                        "new quota file will be empty (%ld)\n", PTR_ERR(f_v1));
215
216         /* mark corresponding quota file as correct */
217         if (!rc)
218                 lustre_init_quota_header(lqi, type, 0);
219
220         RETURN(rc);
221 }
222 EXPORT_SYMBOL(lustre_quota_convert);
223
224 #ifdef HAVE_QUOTA64
225 /*
226  * convert operational quota files to the requested version 
227  * returns: -ESTALE if upgrading to qfmt version is not supported
228  *          -ENOMEM if memory was not allocated for conv. structures
229  *
230  *          other error codes can be returned by VFS and have the
231  *          appropriate meaning
232  */
233 int lustre_slave_quota_convert(lustre_quota_version_t qfmt, int type)
234 {
235         struct lustre_quota_info *lqi;
236         struct file *f_v1, *f_v2;
237         const char *name[][MAXQUOTAS] = LUSTRE_OPQFILES_NAMES;
238         int rc;
239
240         ENTRY;
241
242         /* we convert only to v2 version */
243         if (qfmt != LUSTRE_QUOTA_V2)
244                 GOTO(out, rc = -ESTALE);
245
246         OBD_ALLOC_PTR(lqi);
247         if (lqi == NULL)
248                 GOTO(out, rc = -ENOMEM);
249
250         /* now that we support only v1 and v2 formats,
251          * only upgrade from v1 is possible,
252          * let's check if v1 file exists so that we convert it to v2 */
253         f_v1 = filp_open(name[LUSTRE_QUOTA_V1][type], O_RDONLY, 0);
254         if (IS_ERR(f_v1))
255                 GOTO(out_free, rc = PTR_ERR(f_v1));
256
257         /* make sure it is really a v1 file */
258         if (check_quota_file(f_v1, NULL, type, LUSTRE_QUOTA_V1))
259                 GOTO(out_f_v1, rc = -EINVAL);
260
261         /* create new quota file for v2 version, follow the same rationale as
262          * mds_admin_quota_on: if the file already exists, then do not try to
263          * overwrite it, user has to fix the quotaon issue manually,
264          * e.g. through running quotacheck                                  */
265         f_v2 = filp_open(name[LUSTRE_QUOTA_V2][type],
266                          O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0644);
267         if (IS_ERR(f_v2))
268                 GOTO(out_f_v1, rc = PTR_ERR(f_v2));
269
270         lqi->qi_version = LUSTRE_QUOTA_V2;
271         lqi->qi_files[type] = f_v2;
272
273         /* initialize quota file with defaults, marking it invalid,
274          * this will help us not to get confused with partially converted
275          * operational quota files if we crash during conversion   */
276         rc = lustre_init_quota_info_generic(lqi, type, 1);
277         if (rc)
278                 GOTO(out_f_v2, rc);
279
280         rc = quota_convert_v1_to_v2(f_v1, f_v2, lqi, type);
281         if (!rc) {
282                 /* we dont want good magic to store before the quota data,
283                  * just to be safe if ldiskfs is running in writeback mode */
284                 LOCK_INODE_MUTEX(f_v2->f_dentry->d_inode);
285                 rc = lustre_fsync(f_v2);
286                 if (rc)
287                         CERROR("error from fsync, rc=%d\n", rc);
288                 UNLOCK_INODE_MUTEX(f_v2->f_dentry->d_inode);
289
290                 /* now that conversion successfully finished we mark
291                  * this operational quota file with the correct magic,
292                  * since this moment quotaon will treat it as a correct
293                  * quota file */
294                 rc = lustre_init_quota_header(lqi, type, 0);
295         }
296
297         EXIT;
298
299 out_f_v2:
300         filp_close(f_v2, 0);
301 out_f_v1:
302         filp_close(f_v1, 0);
303 out_free:
304         OBD_FREE_PTR(lqi);
305 out:
306         return rc;
307 }
308 EXPORT_SYMBOL(lustre_slave_quota_convert);
309 #endif /* HAVE_QUOTA64 */
310 #endif /* HAVE_QUOTA_SUPPORT */