Whamcloud - gitweb
4dbfa07d47eff6844181cf8ee494aa5b17a09f48
[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) 2011, 2012, Whamcloud, Inc.
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 = cfs_alloc(LUSTRE_DQBLKSIZE, CFS_ALLOC_IO);
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         cfs_free(buf);
52 }
53
54 /**
55  * Read the \a blk into \a buf.
56  *
57  * TODO Will support enforcement quota later.
58  */
59 static ssize_t quota_read_blk(const struct lu_env *env,
60                               struct osd_object *obj,
61                               int type, uint blk, dqbuf_t buf)
62 {
63         ssize_t ret;
64         struct super_block *sb = obj->oo_inode->i_sb;
65
66         ENTRY;
67
68         memset(buf, 0, LUSTRE_DQBLKSIZE);
69         ret = sb->s_op->quota_read(sb, type, buf, LUSTRE_DQBLKSIZE,
70                                    blk << LUSTRE_DQBLKSIZE_BITS);
71
72         /* Reading past EOF just returns a block of zeros */
73         if (ret == -EBADR)
74                 ret = 0;
75
76         RETURN(ret);
77 }
78
79 /**
80  * Find entry in block by given \a dqid in the leaf block \a blk
81  *
82  * \retval +ve, the offset of the entry in file
83  * \retval   0, entry not found
84  * \retval -ve, unexpected failure
85  */
86 static loff_t find_block_dqentry(const struct lu_env *env,
87                                  struct osd_object *obj, int type,
88                                  qid_t dqid, uint blk,
89                                  struct osd_it_quota *it)
90 {
91         dqbuf_t                          buf = getdqbuf();
92         loff_t                           ret;
93         int                              i;
94         struct lustre_disk_dqblk_v2     *ddquot;
95         int                              dqblk_sz;
96
97         ENTRY;
98
99         ddquot = (struct lustre_disk_dqblk_v2 *)GETENTRIES(buf);
100         dqblk_sz = sizeof(struct lustre_disk_dqblk_v2);
101         if (!buf)
102                 RETURN(-ENOMEM);
103         ret = quota_read_blk(env, obj, type, blk, buf);
104         if (ret < 0) {
105                 CERROR("Can't read quota tree block %u.\n", blk);
106                 GOTO(out_buf, ret);
107         }
108
109         if (dqid) {
110                 for (i = 0; i < LUSTRE_DQSTRINBLK &&
111                             le32_to_cpu(ddquot[i].dqb_id) != dqid; i++)
112                         continue;
113         } else { /* ID 0 as a bit more complicated searching... */
114                 for (i = 0; i < LUSTRE_DQSTRINBLK; i++)
115                         if (!le32_to_cpu(ddquot[i].dqb_id) &&
116                             memcmp((char *)&emptydquot, (char *)&ddquot[i],
117                                    dqblk_sz))
118                                 break;
119         }
120         if (i == LUSTRE_DQSTRINBLK) {
121                 CDEBUG(D_QUOTA, "Quota for id %u not found.\n", dqid);
122                 ret = 0;
123                 GOTO(out_buf, ret);
124         } else {
125                 ret = (blk << LUSTRE_DQBLKSIZE_BITS) +
126                       sizeof(struct lustre_disk_dqdbheader) + i * dqblk_sz;
127
128                 if (it) {
129                         it->oiq_blk[LUSTRE_DQTREEDEPTH - 1] = blk;
130                         it->oiq_offset = ret;
131                         it->oiq_id = dqid;
132                 } else {
133                         ret = 0;
134                 }
135         }
136 out_buf:
137         freedqbuf(buf);
138         RETURN(ret);
139 }
140
141 /**
142  * Find entry for given \a dqid in the tree block \a blk
143  *
144  * \retval +ve, the offset of the entry in file
145  * \retval   0, entry not found
146  * \retval -ve, unexpected failure
147  */
148 loff_t find_tree_dqentry(const struct lu_env *env,
149                          struct osd_object *obj, int type,
150                          qid_t dqid, uint blk, int depth,
151                          struct osd_it_quota *it)
152 {
153         dqbuf_t  buf = getdqbuf();
154         loff_t   ret;
155         u32     *ref = (u32 *) buf;
156
157         ENTRY;
158
159         if (!buf)
160                 RETURN(-ENOMEM);
161         ret = quota_read_blk(env, obj, 0, blk, buf);
162         if (ret < 0) {
163                 CERROR("Can't read quota tree block %u.\n", blk);
164                 GOTO(out_buf, ret);
165         }
166         ret = 0;
167         blk = le32_to_cpu(ref[GETIDINDEX(dqid, depth)]);
168         if (!blk)               /* No reference? */
169                 GOTO(out_buf, ret);
170
171         if (depth < LUSTRE_DQTREEDEPTH - 1)
172                 ret = find_tree_dqentry(env, obj, type, dqid, blk,
173                                         depth + 1, it);
174         else
175                 ret = find_block_dqentry(env, obj, type, dqid, blk, it);
176
177         if (it && ret > 0) /* Entry found */
178                 it->oiq_blk[depth] = blk;
179 out_buf:
180         freedqbuf(buf);
181         RETURN(ret);
182 }
183
184 /**
185  * Search from \a index within the leaf block \a blk, and fill the \a it with
186  * the first valid entry.
187  *
188  * \retval +ve, no valid entry found
189  * \retval   0, entry found
190  * \retval -ve, unexpected failure
191  */
192 int walk_block_dqentry(const struct lu_env *env, struct osd_object *obj,
193                        int type, uint blk, uint index,
194                        struct osd_it_quota *it)
195 {
196         dqbuf_t                          buf = getdqbuf();
197         loff_t                           ret = 0;
198         struct lustre_disk_dqdbheader   *dqhead;
199         int                              i, dqblk_sz;
200         struct lustre_disk_dqblk_v2     *ddquot;
201
202         ENTRY;
203
204         dqhead = (struct lustre_disk_dqdbheader *)buf;
205         dqblk_sz = sizeof(struct lustre_disk_dqblk_v2);
206         if (!buf)
207                 RETURN(-ENOMEM);
208         ret = quota_read_blk(env, obj, type, blk, buf);
209         if (ret < 0) {
210                 CERROR("Can't read quota tree block %u.\n", blk);
211                 GOTO(out_buf, ret);
212         }
213         ret = 1;
214
215         if (!le32_to_cpu(dqhead->dqdh_entries))
216                 GOTO(out_buf, ret);
217
218         ddquot = (struct lustre_disk_dqblk_v2 *)GETENTRIES(buf);
219         LASSERT(index < LUSTRE_DQSTRINBLK);
220         for (i = index; i < LUSTRE_DQSTRINBLK; i++) {
221                 /* skip empty entry */
222                 if (!memcmp((char *)&emptydquot,
223                             (char *)&ddquot[i], dqblk_sz))
224                         continue;
225
226                 it->oiq_blk[LUSTRE_DQTREEDEPTH - 1] = blk;
227                 it->oiq_id = le32_to_cpu(ddquot[i].dqb_id);
228                 it->oiq_offset = (blk << LUSTRE_DQBLKSIZE_BITS) +
229                                   sizeof(struct lustre_disk_dqdbheader) +
230                                   i * dqblk_sz;
231                 ret = 0;
232                 break;
233         }
234
235 out_buf:
236         freedqbuf(buf);
237         RETURN(ret);
238 }
239
240 /**
241  * Search from \a index within the tree block \a blk, and fill the \a it
242  * with the first valid entry.
243  *
244  * \retval +ve, no valid entry found
245  * \retval   0, entry found
246  * \retval -ve, unexpected failure
247  */
248 int walk_tree_dqentry(const struct lu_env *env, struct osd_object *obj,
249                       int type, uint blk, int depth, uint index,
250                       struct osd_it_quota *it)
251 {
252         dqbuf_t  buf = getdqbuf();
253         loff_t   ret;
254         u32     *ref = (u32 *) buf;
255
256         ENTRY;
257
258         if (!buf)
259                 RETURN(-ENOMEM);
260         ret = quota_read_blk(env, obj, type, blk, buf);
261         if (ret < 0) {
262                 CERROR("Can't read quota tree block %u.\n", blk);
263                 goto out_buf;
264         }
265         ret = 1;
266
267         for (; index <= 0xff && ret > 0; index++) {
268                 blk = le32_to_cpu(ref[index]);
269                 if (!blk)       /* No reference */
270                         continue;
271
272                 if (depth < LUSTRE_DQTREEDEPTH - 1)
273                         ret = walk_tree_dqentry(env, obj, type, blk,
274                                                 depth + 1, 0, it);
275                 else
276                         ret = walk_block_dqentry(env, obj, type, blk, 0, it);
277         }
278
279         if (ret == 0) /* Entry found */
280                 it->oiq_blk[depth] = blk;
281 out_buf:
282         freedqbuf(buf);
283         RETURN(ret);
284 }