Whamcloud - gitweb
New tag 2.7.54
[fs/lustre-release.git] / lustre / osd-ldiskfs / osd_quota_fmt.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 021110-1307, USA
20  *
21  * GPL HEADER END
22  */
23 /*
24  * Copyright (c) 2012, 2014, Intel Corporation.
25  * Use is subject to license terms.
26  *
27  * Lustre administrative quota format.
28  * from linux/fs/quota_v2.c
29  */
30
31 #include "osd_internal.h"
32 #include "osd_quota_fmt.h"
33
34 typedef char *dqbuf_t;
35
36 static const union
37 {
38         struct lustre_disk_dqblk_v2 r1;
39 } emptydquot = { .r1 = { 0 } };
40
41 static inline dqbuf_t getdqbuf(void)
42 {
43         dqbuf_t buf = kmalloc(LUSTRE_DQBLKSIZE, GFP_NOFS);
44         if (!buf)
45                 CWARN("Not enough memory for quota buffers.\n");
46         return buf;
47 }
48
49 static inline void freedqbuf(dqbuf_t buf)
50 {
51         kfree(buf);
52 }
53
54 /**
55  * Read the \a blk into \a buf.
56  */
57 static ssize_t quota_read_blk(const struct lu_env *env,
58                               struct osd_object *obj,
59                               int type, uint blk, dqbuf_t buf)
60 {
61         ssize_t ret;
62         struct super_block *sb = obj->oo_inode->i_sb;
63
64         ENTRY;
65
66         memset(buf, 0, LUSTRE_DQBLKSIZE);
67
68 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 7, 56, 0)
69         /* type is set as -1 when reading old admin quota file */
70         if (type != USRQUOTA && type != GRPQUOTA) {
71                 struct lu_buf   lu_buffer;
72                 loff_t          pos;
73
74                 lu_buffer.lb_buf = buf;
75                 lu_buffer.lb_len = LUSTRE_DQBLKSIZE;
76                 pos = blk << LUSTRE_DQBLKSIZE_BITS;
77
78                 ret = dt_record_read(env, &obj->oo_dt, &lu_buffer, &pos);
79
80                 if (ret == 0)
81                         ret = LUSTRE_DQBLKSIZE;
82                 else if (ret == -EBADR || ret == -EFAULT)
83                         ret = 0;
84                 RETURN(ret);
85         }
86 #else
87 #warning "remove old quota compatibility code"
88 #endif
89
90         ret = sb->s_op->quota_read(sb, type, buf, LUSTRE_DQBLKSIZE,
91                                    blk << LUSTRE_DQBLKSIZE_BITS);
92
93         /* Reading past EOF just returns a block of zeros */
94         if (ret == -EBADR)
95                 ret = 0;
96
97         RETURN(ret);
98 }
99
100 /**
101  * Find entry in block by given \a dqid in the leaf block \a blk
102  *
103  * \retval +ve, the offset of the entry in file
104  * \retval   0, entry not found
105  * \retval -ve, unexpected failure
106  */
107 static loff_t find_block_dqentry(const struct lu_env *env,
108                                  struct osd_object *obj, int type,
109                                  qid_t dqid, uint blk,
110                                  struct osd_it_quota *it)
111 {
112         dqbuf_t                          buf = getdqbuf();
113         loff_t                           ret;
114         int                              i;
115         struct lustre_disk_dqblk_v2     *ddquot;
116         int                              dqblk_sz;
117
118         ENTRY;
119
120         ddquot = (struct lustre_disk_dqblk_v2 *)GETENTRIES(buf);
121         dqblk_sz = sizeof(struct lustre_disk_dqblk_v2);
122         if (!buf)
123                 RETURN(-ENOMEM);
124         ret = quota_read_blk(env, obj, type, blk, buf);
125         if (ret < 0) {
126                 CERROR("Can't read quota tree block %u.\n", blk);
127                 GOTO(out_buf, ret);
128         }
129
130         if (dqid) {
131                 for (i = 0; i < LUSTRE_DQSTRINBLK &&
132                             le32_to_cpu(ddquot[i].dqb_id) != dqid; i++)
133                         continue;
134         } else { /* ID 0 as a bit more complicated searching... */
135                 for (i = 0; i < LUSTRE_DQSTRINBLK; i++)
136                         if (!le32_to_cpu(ddquot[i].dqb_id) &&
137                             memcmp((char *)&emptydquot, (char *)&ddquot[i],
138                                    dqblk_sz))
139                                 break;
140         }
141         if (i == LUSTRE_DQSTRINBLK) {
142                 CDEBUG(D_QUOTA, "Quota for id %u not found.\n", dqid);
143                 ret = 0;
144                 GOTO(out_buf, ret);
145         } else {
146                 ret = (blk << LUSTRE_DQBLKSIZE_BITS) +
147                       sizeof(struct lustre_disk_dqdbheader) + i * dqblk_sz;
148
149                 if (it) {
150                         it->oiq_blk[LUSTRE_DQTREEDEPTH] = blk;
151                         it->oiq_offset = ret;
152                         it->oiq_id = dqid;
153                         it->oiq_index[LUSTRE_DQTREEDEPTH] = i;
154                 } else {
155                         ret = 0;
156                 }
157         }
158 out_buf:
159         freedqbuf(buf);
160         RETURN(ret);
161 }
162
163 /**
164  * Find entry for given \a dqid in the tree block \a blk
165  *
166  * \retval +ve, the offset of the entry in file
167  * \retval   0, entry not found
168  * \retval -ve, unexpected failure
169  */
170 loff_t find_tree_dqentry(const struct lu_env *env,
171                          struct osd_object *obj, int type,
172                          qid_t dqid, uint blk, int depth,
173                          struct osd_it_quota *it)
174 {
175         dqbuf_t  buf = getdqbuf();
176         loff_t   ret;
177         u32     *ref = (u32 *) buf;
178
179         ENTRY;
180
181         if (!buf)
182                 RETURN(-ENOMEM);
183         ret = quota_read_blk(env, obj, type, blk, buf);
184         if (ret < 0) {
185                 CERROR("Can't read quota tree block %u.\n", blk);
186                 GOTO(out_buf, ret);
187         }
188         ret = 0;
189         blk = le32_to_cpu(ref[GETIDINDEX(dqid, depth)]);
190         if (!blk)               /* No reference? */
191                 GOTO(out_buf, ret);
192
193         if (depth < LUSTRE_DQTREEDEPTH - 1)
194                 ret = find_tree_dqentry(env, obj, type, dqid, blk,
195                                         depth + 1, it);
196         else
197                 ret = find_block_dqentry(env, obj, type, dqid, blk, it);
198
199         if (it && ret > 0) {
200                 it->oiq_blk[depth + 1] = blk;
201                 it->oiq_index[depth] = GETIDINDEX(dqid, depth);
202         }
203
204 out_buf:
205         freedqbuf(buf);
206         RETURN(ret);
207 }
208
209 /**
210  * Search from \a index within the leaf block \a blk, and fill the \a it with
211  * the first valid entry.
212  *
213  * \retval +ve, no valid entry found
214  * \retval   0, entry found
215  * \retval -ve, unexpected failure
216  */
217 int walk_block_dqentry(const struct lu_env *env, struct osd_object *obj,
218                        int type, uint blk, uint index,
219                        struct osd_it_quota *it)
220 {
221         dqbuf_t                          buf;
222         loff_t                           ret = 0;
223         struct lustre_disk_dqdbheader   *dqhead;
224         int                              i, dqblk_sz;
225         struct lustre_disk_dqblk_v2     *ddquot;
226         struct osd_quota_leaf           *leaf;
227         ENTRY;
228
229         /* check if the leaf block has been processed before */
230         list_for_each_entry(leaf, &it->oiq_list, oql_link) {
231                 if (leaf->oql_blk == blk)
232                         RETURN(1);
233         }
234
235         buf = getdqbuf();
236         dqhead = (struct lustre_disk_dqdbheader *)buf;
237         dqblk_sz = sizeof(struct lustre_disk_dqblk_v2);
238         if (!buf)
239                 RETURN(-ENOMEM);
240         ret = quota_read_blk(env, obj, type, blk, buf);
241         if (ret < 0) {
242                 CERROR("Can't read quota tree block %u.\n", blk);
243                 GOTO(out_buf, ret);
244         }
245         ret = 1;
246
247         if (!le16_to_cpu(dqhead->dqdh_entries))
248                 GOTO(out_buf, ret);
249
250         ddquot = (struct lustre_disk_dqblk_v2 *)GETENTRIES(buf);
251         LASSERT(index < LUSTRE_DQSTRINBLK);
252         for (i = index; i < LUSTRE_DQSTRINBLK; i++) {
253                 /* skip empty entry */
254                 if (!memcmp((char *)&emptydquot,
255                             (char *)&ddquot[i], dqblk_sz))
256                         continue;
257
258                 it->oiq_blk[LUSTRE_DQTREEDEPTH] = blk;
259                 it->oiq_id = le32_to_cpu(ddquot[i].dqb_id);
260                 it->oiq_offset = (blk << LUSTRE_DQBLKSIZE_BITS) +
261                                   sizeof(struct lustre_disk_dqdbheader) +
262                                   i * dqblk_sz;
263                 it->oiq_index[LUSTRE_DQTREEDEPTH] = i;
264                 ret = 0;
265                 break;
266         }
267
268 out_buf:
269         freedqbuf(buf);
270         RETURN(ret);
271 }
272
273 /**
274  * Search from \a index within the tree block \a blk, and fill the \a it
275  * with the first valid entry.
276  *
277  * \retval +ve, no valid entry found
278  * \retval   0, entry found
279  * \retval -ve, unexpected failure
280  */
281 int walk_tree_dqentry(const struct lu_env *env, struct osd_object *obj,
282                       int type, uint blk, int depth, uint index,
283                       struct osd_it_quota *it)
284 {
285         dqbuf_t  buf = getdqbuf();
286         loff_t   ret;
287         u32     *ref = (u32 *) buf;
288
289         ENTRY;
290
291         if (!buf)
292                 RETURN(-ENOMEM);
293         ret = quota_read_blk(env, obj, type, blk, buf);
294         if (ret < 0) {
295                 CERROR("Can't read quota tree block %u.\n", blk);
296                 goto out_buf;
297         }
298         ret = 1;
299
300         for (; index <= 0xff && ret > 0; index++) {
301                 blk = le32_to_cpu(ref[index]);
302                 if (!blk)       /* No reference */
303                         continue;
304
305                 if (depth < LUSTRE_DQTREEDEPTH - 1)
306                         ret = walk_tree_dqentry(env, obj, type, blk,
307                                                 depth + 1, 0, it);
308                 else
309                         ret = walk_block_dqentry(env, obj, type, blk, 0, it);
310         }
311
312         if (ret == 0) { /* Entry found */
313                 it->oiq_blk[depth + 1] = blk;
314                 it->oiq_index[depth] = index;
315         }
316
317 out_buf:
318         freedqbuf(buf);
319         RETURN(ret);
320 }