Whamcloud - gitweb
6421bc423a2b0097eea4e75429acf75f02441350
[fs/lustre-release.git] / lustre / quota / lquota_disk.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 Intel, Inc.
25  * Use is subject to license terms.
26  *
27  * Author: Johann Lombardi <johann.lombardi@intel.com>
28  * Author: Niu    Yawei    <yawei.niu@intel.com>
29  */
30
31 /*
32  * The disk API is used by both the QMT and QSD to access/update on-disk index
33  * files. The API consists of the following functions:
34  *
35  * - lquota_disk_dir_find_create: look-up quota directory, create it if not
36  *                                found.
37  * - lquota_disk_glb_find_create: look-up global index file, create it if not
38  *                                found.
39  * - lquota_disk_slv_find:        look-up a slave index file.
40  * - lquota_disk_slv_find_create: look-up a slave index file. Allocate a FID if
41  *                                required and create the index file on disk if
42  *                                it does not exist.
43  * - lquota_disk_for_each_slv:    iterate over all existing slave index files
44  * - lquota_disk_read:            read quota settings from a index file
45  * - lquota_disk_declare_write:   reserve credits to update a record in an index
46  *                                file
47  * - lquota_disk_write:           update a record in an index file
48  * - lquota_disk_update_ver:      update version of an index file
49  */
50
51 #ifndef EXPORT_SYMTAB
52 # define EXPORT_SYMTAB
53 #endif
54
55 #define DEBUG_SUBSYSTEM S_LQUOTA
56
57 #include "lquota_internal.h"
58
59 #define LQUOTA_MODE (S_IFREG | S_IRUGO | S_IWUSR)
60
61 /*
62  * Helper function looking up & creating if not found an index file with a
63  * dynamic fid.
64  */
65 static struct dt_object *
66 lquota_disk_find_create(const struct lu_env *env, struct dt_device *dev,
67                         struct dt_object *parent, struct lu_fid *fid,
68                         const struct dt_index_features *idx_feat,
69                         char *name)
70 {
71         struct lquota_thread_info       *qti = lquota_info(env);
72         struct dt_object                *obj;
73         struct local_oid_storage        *los;
74         int                              rc;
75         ENTRY;
76
77         /* Set up local storage */
78         rc = local_oid_storage_init(env, dev, fid, &los);
79         if (rc)
80                 RETURN(ERR_PTR(rc));
81
82         /* lookup/create slave index file */
83         obj = local_index_find_or_create(env, los, parent, name, LQUOTA_MODE,
84                                          idx_feat);
85         if (IS_ERR(obj))
86                 GOTO(out, obj);
87
88         /* local_oid_storage_fini() will finalize the local storage device,
89          * we have to open the object in another device stack */
90         qti->qti_fid = obj->do_lu.lo_header->loh_fid;
91         lu_object_put_nocache(env, &obj->do_lu);
92         obj = dt_locate(env, dev, &qti->qti_fid);
93         if (IS_ERR(obj))
94                 GOTO(out, obj);
95
96         /* install index operation vector */
97         if (obj->do_index_ops == NULL) {
98                 rc = obj->do_ops->do_index_try(env, obj, idx_feat);
99                 if (rc) {
100                         CERROR("%s: fail to setup index operations for "DFID
101                                " rc:%d\n", dev->dd_lu_dev.ld_obd->obd_name,
102                                PFID(lu_object_fid(&obj->do_lu)), rc);
103                         lu_object_put(env, &obj->do_lu);
104                         obj = ERR_PTR(rc);
105                 }
106         }
107 out:
108         local_oid_storage_fini(env, los);
109         RETURN(obj);
110 }
111
112 /*
113  * helper function to generate the filename associated with a slave index file
114  */
115 static inline int lquota_disk_slv_filename(struct lu_fid *glb_fid,
116                                            struct obd_uuid *uuid,
117                                            char *filename)
118 {
119         char    *name, *uuid_str;
120
121         /* In most case, the uuid is NULL terminated */
122         if (uuid->uuid[sizeof(*uuid) - 1] != '\0') {
123                 OBD_ALLOC(uuid_str, sizeof(*uuid));
124                 if (uuid_str == NULL)
125                         RETURN(-ENOMEM);
126                 memcpy(uuid_str, uuid->uuid, sizeof(*uuid) - 1);
127         } else {
128                 uuid_str = (char *)uuid->uuid;
129         }
130
131         /* we strip the slave's UUID (in the form of fsname-OST0001_UUID) of
132          * the filesystem name in case this one is changed in the future */
133         name = strrchr(uuid_str, '-');
134         if (name == NULL) {
135                 name = strrchr(uuid_str, ':');
136                 if (name == NULL) {
137                         CERROR("Failed to extract extract filesystem "
138                                "name from UUID %s\n", uuid_str);
139                         if (uuid_str != uuid->uuid)
140                                 OBD_FREE(uuid_str, sizeof(*uuid));
141                         return -EINVAL;
142                 }
143         }
144         name++;
145
146         /* the filename is composed of the most signicant bits of the global
147          * FID, that's to say the oid which encodes the pool id, pool type and
148          * quota type, followed by the export UUID */
149         sprintf(filename, "0x%x-%s", glb_fid->f_oid, name);
150
151         if (uuid_str != uuid->uuid)
152                 OBD_FREE(uuid_str, sizeof(*uuid));
153
154         return 0;
155 }
156
157 /*
158  * Set up quota directory (either "quota_master" or "quota_slave") for a QMT or
159  * QSD instance. This function is also used to create per-pool directory on
160  * the quota master.
161  * The directory is created with a local sequence if it does not exist already.
162  * This function is called at ->ldo_prepare time when the full device stack is
163  * configured.
164  *
165  * \param env  - is the environment passed by the caller
166  * \param dev  - is the dt_device where to create the quota directory
167  * \param parent  - is the parent directory. If not specified, the directory
168  *                  will be created under the root directory
169  * \param name - is the name of quota directory to be created
170  *
171  * \retval     - pointer to quota root dt_object on success, appropriate error
172  *               on failure
173  */
174 struct dt_object *lquota_disk_dir_find_create(const struct lu_env *env,
175                                               struct dt_device *dev,
176                                               struct dt_object *parent,
177                                               const char *name)
178 {
179         struct lquota_thread_info       *qti = lquota_info(env);
180         struct dt_object                *qt_dir = NULL;
181         struct local_oid_storage        *los = NULL;
182         int                              rc;
183         ENTRY;
184
185         /* Set up local storage to create the quota directory.
186          * We use the sequence reserved for local named objects */
187         lu_local_name_obj_fid(&qti->qti_fid, 1);
188         rc = local_oid_storage_init(env, dev, &qti->qti_fid, &los);
189         if (rc)
190                 RETURN(ERR_PTR(rc));
191
192         if (parent == NULL) {
193                 /* Fetch dt object associated with root directory */
194                 rc = dt_root_get(env, dev, &qti->qti_fid);
195                 if (rc)
196                         GOTO(out, rc);
197
198                 parent = dt_locate_at(env, dev, &qti->qti_fid,
199                                       dev->dd_lu_dev.ld_site->ls_top_dev);
200                 if (IS_ERR(parent))
201                         GOTO(out, rc = PTR_ERR(parent));
202         } else {
203                 lu_object_get(&parent->do_lu);
204         }
205
206         /* create quota directory to be used for all quota index files */
207         qt_dir = local_file_find_or_create(env, los, parent, name, S_IFDIR |
208                                            S_IRUGO | S_IWUSR | S_IXUGO);
209         if (IS_ERR(qt_dir))
210                 GOTO(out, rc = PTR_ERR(qt_dir));
211
212         /* local_oid_storage_fini() will finalize the local storage device,
213          * we have to open the object in another device stack */
214         qti->qti_fid = qt_dir->do_lu.lo_header->loh_fid;
215         lu_object_put_nocache(env, &qt_dir->do_lu);
216         qt_dir = dt_locate(env, dev, &qti->qti_fid);
217         if (IS_ERR(qt_dir))
218                 GOTO(out, rc = PTR_ERR(qt_dir));
219
220         if (!dt_try_as_dir(env, qt_dir))
221                 GOTO(out, rc = -ENOTDIR);
222         EXIT;
223 out:
224         if (parent != NULL && !IS_ERR(parent))
225                 lu_object_put(env, &parent->do_lu);
226         if (los != NULL)
227                 local_oid_storage_fini(env, los);
228         if (rc) {
229                 if (qt_dir != NULL && !IS_ERR(qt_dir))
230                         lu_object_put(env, &qt_dir->do_lu);
231                 qt_dir = ERR_PTR(rc);
232         }
233         return qt_dir;
234 }
235
236 /*
237  * Look-up/create a global index file.
238  *
239  * \param env - is the environment passed by the caller
240  * \parap dev - is the dt_device where to lookup/create the global index file
241  * \param parent - is the parent directory where to create the global index if
242  *                 not found
243  * \param fid - is the fid of the global index to be looked up/created
244  * \parap local - indicates whether the index should be created with a local
245  *                generated fid or with \fid
246  *
247  * \retval     - pointer to the dt_object of the global index on success,
248  *               appropriate error on failure
249  */
250 struct dt_object *lquota_disk_glb_find_create(const struct lu_env *env,
251                                               struct dt_device *dev,
252                                               struct dt_object *parent,
253                                               struct lu_fid *fid, bool local)
254 {
255         struct lquota_thread_info       *qti = lquota_info(env);
256         struct dt_object                *glb_idx;
257         const struct dt_index_features  *idx_feat;
258         ENTRY;
259
260         CDEBUG(D_QUOTA, "look-up/create %sglobal idx file ("DFID")\n",
261                local ? "local " : "", PFID(fid));
262
263 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2,7,50,0)
264         /* we use different index feature for each quota type and target type
265          * for the time being. This is done for on-disk conversion from the old
266          * quota format. Once this is no longer required, we should just be
267          * using dt_quota_glb_features for all global index file */
268         idx_feat = glb_idx_feature(fid);
269 #else
270 #warning "remove old quota compatibility code"
271         idx_feat = &dt_quota_glb_features;
272 #endif
273
274         /* the filename is composed of the most signicant bits of the FID,
275          * that's to say the oid which encodes the pool id, pool type and quota
276          * type */
277         sprintf(qti->qti_buf, "0x%x", fid->f_oid);
278
279         if (local) {
280                 /* We use the sequence reserved for local named objects */
281                 lu_local_name_obj_fid(&qti->qti_fid, 1);
282                 glb_idx = lquota_disk_find_create(env, dev, parent,
283                                                   &qti->qti_fid, idx_feat,
284                                                   qti->qti_buf);
285         } else {
286                 /* look-up/create global index on disk */
287                 glb_idx = local_index_find_or_create_with_fid(env, dev, fid,
288                                                               parent,
289                                                               qti->qti_buf,
290                                                               LQUOTA_MODE,
291                                                               idx_feat);
292         }
293
294         if (IS_ERR(glb_idx))
295                 CERROR("%s: failed to look-up/create idx file "DFID" rc:%ld "
296                        "local:%d\n", dev->dd_lu_dev.ld_obd->obd_name,
297                        PFID(fid), PTR_ERR(glb_idx), local);
298
299         RETURN(glb_idx);
300 }
301
302 /*
303  * Look-up a slave index file.
304  *
305  * \param env - is the environment passed by the caller
306  * \param dev - is the backend dt_device where to look-up/create the slave index
307  * \param parent - is the parent directory where to lookup the slave index
308  * \param glb_fid - is the fid of the global index file associated with this
309  *                  slave index.
310  * \param uuid    - is the uuid of slave which is (re)connecting to the master
311  *                  target
312  *
313  * \retval     - pointer to the dt_object of the slave index on success,
314  *               appropriate error on failure
315  */
316 struct dt_object *lquota_disk_slv_find(const struct lu_env *env,
317                                        struct dt_device *dev,
318                                        struct dt_object *parent,
319                                        struct lu_fid *glb_fid,
320                                        struct obd_uuid *uuid)
321 {
322         struct lquota_thread_info       *qti = lquota_info(env);
323         struct dt_object                *slv_idx;
324         int                              rc;
325         ENTRY;
326
327         LASSERT(uuid != NULL);
328
329         CDEBUG(D_QUOTA, "lookup slave index file for %s\n",
330                obd_uuid2str(uuid));
331
332         /* generate filename associated with the slave */
333         rc = lquota_disk_slv_filename(glb_fid, uuid, qti->qti_buf);
334         if (rc)
335                 RETURN(ERR_PTR(rc));
336
337         /* lookup slave index file */
338         rc = dt_lookup_dir(env, parent, qti->qti_buf, &qti->qti_fid);
339         if (rc)
340                 RETURN(ERR_PTR(rc));
341
342         /* name is found, get the object */
343         slv_idx = dt_locate(env, dev, &qti->qti_fid);
344         if (IS_ERR(slv_idx))
345                 RETURN(slv_idx);
346
347         if (slv_idx->do_index_ops == NULL) {
348                 rc = slv_idx->do_ops->do_index_try(env, slv_idx,
349                                                    &dt_quota_slv_features);
350                 if (rc) {
351                         CERROR("%s: failed to setup slave index operations for "
352                                "%s, rc:%d\n", dev->dd_lu_dev.ld_obd->obd_name,
353                                obd_uuid2str(uuid), rc);
354                         lu_object_put(env, &slv_idx->do_lu);
355                         slv_idx = ERR_PTR(rc);
356                 }
357         }
358
359         RETURN(slv_idx);
360 }
361
362 /*
363  * Look-up a slave index file. If the slave index isn't found:
364  * - if local is set to false, we allocate a FID from FID_SEQ_QUOTA sequence and
365  *   create the index.
366  * - otherwise, we create the index file with a local reserved FID (see
367  *   lquota_local_oid)
368  *
369  * \param env - is the environment passed by the caller
370  * \param dev - is the backend dt_device where to look-up/create the slave index
371  * \param parent - is the parent directory where to create the slave index if
372  *                 it does not exist already
373  * \param glb_fid - is the fid of the global index file associated with this
374  *                  slave index.
375  * \param uuid    - is the uuid of slave which is (re)connecting to the master
376  *                  target
377  * \param local   - indicate whether to use local reserved FID (LQUOTA_USR_OID
378  *                  & LQUOTA_GRP_OID) for the slave index creation or to
379  *                  allocate a new fid from sequence FID_SEQ_QUOTA
380  *
381  * \retval     - pointer to the dt_object of the slave index on success,
382  *               appropriate error on failure
383  */
384 struct dt_object *lquota_disk_slv_find_create(const struct lu_env *env,
385                                               struct dt_device *dev,
386                                               struct dt_object *parent,
387                                               struct lu_fid *glb_fid,
388                                               struct obd_uuid *uuid,
389                                               bool local)
390 {
391         struct lquota_thread_info       *qti = lquota_info(env);
392         struct dt_object                *slv_idx;
393         int                              rc;
394         ENTRY;
395
396         LASSERT(uuid != NULL);
397
398         CDEBUG(D_QUOTA, "lookup/create slave index file for %s\n",
399                obd_uuid2str(uuid));
400
401         /* generate filename associated with the slave */
402         rc = lquota_disk_slv_filename(glb_fid, uuid, qti->qti_buf);
403         if (rc)
404                 RETURN(ERR_PTR(rc));
405
406         /* Slave indexes uses the FID_SEQ_QUOTA sequence since they can be read
407          * through the network */
408         qti->qti_fid.f_seq = FID_SEQ_QUOTA;
409         qti->qti_fid.f_ver = 0;
410         if (local) {
411                 int type;
412
413                 rc = lquota_extract_fid(glb_fid, NULL, NULL, &type);
414                 if (rc)
415                         RETURN(ERR_PTR(rc));
416
417                 /* use predefined fid in the reserved oid list */
418                 qti->qti_fid.f_oid = (type == USRQUOTA) ? LQUOTA_USR_OID
419                                                         : LQUOTA_GRP_OID;
420
421                 slv_idx = local_index_find_or_create_with_fid(env, dev,
422                                                               &qti->qti_fid,
423                                                               parent,
424                                                               qti->qti_buf,
425                                                               LQUOTA_MODE,
426                                                         &dt_quota_slv_features);
427         } else {
428                 /* allocate fid dynamically if index does not exist already */
429                 qti->qti_fid.f_oid = LQUOTA_GENERATED_OID;
430
431                 /* lookup/create slave index file */
432                 slv_idx = lquota_disk_find_create(env, dev, parent,
433                                                   &qti->qti_fid,
434                                                   &dt_quota_slv_features,
435                                                   qti->qti_buf);
436         }
437
438         RETURN(slv_idx);
439 }
440
441 /*
442  * Iterate over all slave index files associated with global index \glb_fid and
443  * invoke a callback function for each slave index file.
444  *
445  * \param env     - is the environment passed by the caller
446  * \param parent  - is the parent directory where the slave index files are
447  *                  stored
448  * \param glb_fid - is the fid of the global index file associated with the
449  *                  slave indexes to scan
450  * \param func    - is the callback function to call each time a slave index
451  *                  file is found
452  * \param arg     - is an opaq argument passed to the callback function \func
453  */
454 int lquota_disk_for_each_slv(const struct lu_env *env, struct dt_object *parent,
455                              struct lu_fid *glb_fid, lquota_disk_slv_cb_t func,
456                              void *arg)
457 {
458         struct lquota_thread_info       *qti = lquota_info(env);
459         struct dt_it                    *it;
460         const struct dt_it_ops          *iops;
461         char                            *name;
462         int                              rc;
463         ENTRY;
464
465         OBD_ALLOC(name, sizeof("0x00000000-"));
466         if (name == NULL)
467                 RETURN(-ENOMEM);
468
469         /* filename associated with slave index files are prefixed with the most
470          * signicant bits of the global FID */
471         sprintf(name, "0x%x-", glb_fid->f_oid);
472
473         iops = &parent->do_index_ops->dio_it;
474         it = iops->init(env, parent, 0, BYPASS_CAPA);
475         if (IS_ERR(it)) {
476                 OBD_FREE(name, sizeof("0x00000000-"));
477                 RETURN(PTR_ERR(it));
478         }
479
480         rc = iops->load(env, it, 0);
481         if (rc == 0) {
482                 /*
483                  * Iterator didn't find record with exactly the key requested.
484                  *
485                  * It is currently either
486                  *
487                  *     - positioned above record with key less than
488                  *     requested---skip it.
489                  *
490                  *     - or not positioned at all (is in IAM_IT_SKEWED
491                  *     state)---position it on the next item.
492                  */
493                 rc = iops->next(env, it);
494         } else if (rc > 0)
495                 rc = 0;
496
497         while (rc == 0) {
498                 struct dt_key   *key;
499                 int              len;
500
501                 len = iops->key_size(env, it);
502                 /* IAM iterator can return record with zero len. */
503                 if (len == 0 || len <= strlen(name) || len >= LQUOTA_NAME_MAX)
504                         goto next;
505
506                 key = iops->key(env, it);
507                 if (IS_ERR(key)) {
508                         rc = PTR_ERR(key);
509                         break;
510                 }
511
512                 if (strncmp((char *)key, name, strlen(name)) != 0)
513                         goto next;
514
515                 /* ldiskfs OSD returns filename as stored in directory entry
516                  * which does not end up with '\0' */
517                 memcpy(&qti->qti_buf, key, len);
518                 qti->qti_buf[len] = '\0';
519
520                 /* lookup fid associated with this slave index file */
521                 rc = dt_lookup_dir(env, parent, qti->qti_buf, &qti->qti_fid);
522                 if (rc)
523                         break;
524
525                 if (qti->qti_fid.f_seq != FID_SEQ_QUOTA)
526                         goto next;
527
528                 rc = func(env, glb_fid, (char *)key, &qti->qti_fid, arg);
529                 if (rc)
530                         break;
531 next:
532                 do {
533                         rc = iops->next(env, it);
534                 } while (rc == -ESTALE);
535         }
536
537         iops->put(env, it);
538         iops->fini(env, it);
539         OBD_FREE(name, sizeof("0x00000000-"));
540         if (rc > 0)
541                 rc = 0;
542         RETURN(rc);
543 }
544
545 /*
546  * Retrieve quota settings from disk for a particular identifier.
547  *
548  * \param env - is the environment passed by the caller
549  * \param obj - is the on-disk index where quota settings are stored.
550  * \param id  - is the key to be updated
551  * \param rec - is the output record where to store quota settings.
552  *
553  * \retval    - 0 on success, appropriate error on failure
554  */
555 int lquota_disk_read(const struct lu_env *env, struct dt_object *obj,
556                      union lquota_id *id, struct dt_rec *rec)
557 {
558         int     rc;
559         ENTRY;
560
561         LASSERT(dt_object_exists(obj));
562         LASSERT(obj->do_index_ops != NULL);
563
564         /* lookup on-disk record from index file */
565         dt_read_lock(env, obj, 0);
566         rc = dt_lookup(env, obj, rec, (struct dt_key *)&id->qid_uid,
567                        BYPASS_CAPA);
568         dt_read_unlock(env, obj);
569
570         RETURN(rc);
571 }
572
573 /*
574  * Reserve enough credits to update a record in a quota index file.
575  *
576  * \param env - is the environment passed by the caller
577  * \param th  - is the transaction to use for disk writes
578  * \param obj - is the on-disk index where quota settings are stored.
579  * \param id  - is the key to be updated
580  *
581  * \retval    - 0 on success, appropriate error on failure
582  */
583 int lquota_disk_declare_write(const struct lu_env *env, struct thandle *th,
584                               struct dt_object *obj, union lquota_id *id)
585 {
586         struct lquota_thread_info       *qti = lquota_info(env);
587         struct dt_key                   *key = (struct dt_key *)&id->qid_uid;
588         int                              rc;
589         ENTRY;
590
591         LASSERT(dt_object_exists(obj));
592         LASSERT(obj->do_index_ops != NULL);
593
594         /* speculative delete declaration in case there is already an existing
595          * record in the index */
596         rc = dt_declare_delete(env, obj, key, th);
597         if (rc)
598                 RETURN(rc);
599
600         /* declare insertion of updated record */
601         rc = dt_declare_insert(env, obj, (struct dt_rec *)&qti->qti_rec, key,
602                                th);
603         if (rc)
604                 RETURN(rc);
605
606         /* we might have to update the version of the global index too */
607         rc = dt_declare_version_set(env, obj, th);
608
609         RETURN(rc);
610 }
611
612 /*
613  * Update a record in a quota index file.
614  *
615  * \param env - is the environment passed by the caller
616  * \param th  - is the transaction to use for disk writes
617  * \param obj - is the on-disk index to be updated.
618  * \param id  - is the key to be updated
619  * \param rec - is the input record containing the new quota settings.
620  * \param flags - can be LQUOTA_BUMP_VER or LQUOTA_SET_VER.
621  * \param ver   - is the new version of the index if LQUOTA_SET_VER is set or is
622  *                used to return the new version of the index when
623  *                LQUOTA_BUMP_VER is set.
624  *
625  * \retval    - 0 on success, appropriate error on failure
626  */
627 int lquota_disk_write(const struct lu_env *env, struct thandle *th,
628                       struct dt_object *obj, union lquota_id *id,
629                       struct dt_rec *rec, __u32 flags, __u64 *ver)
630 {
631         struct lquota_thread_info       *qti = lquota_info(env);
632         struct dt_key                   *key = (struct dt_key *)&id->qid_uid;
633         int                              rc;
634         ENTRY;
635
636         LASSERT(dt_object_exists(obj));
637         LASSERT(obj->do_index_ops != NULL);
638
639         /* lock index */
640         dt_write_lock(env, obj, 0);
641
642         /* check whether there is already an existing record for this ID */
643         rc = dt_lookup(env, obj, (struct dt_rec *)&qti->qti_rec, key,
644                        BYPASS_CAPA);
645         if (rc == 0) {
646                 /* delete existing record in order to replace it */
647                 rc = dt_delete(env, obj, key, th, BYPASS_CAPA);
648                 if (rc)
649                         GOTO(out, rc);
650         } else if (rc == -ENOENT) {
651                 /* probably first insert */
652                 rc = 0;
653         } else {
654                 GOTO(out, rc);
655         }
656
657         if (rec != NULL) {
658                 /* insert record with updated quota settings */
659                 rc = dt_insert(env, obj, rec, key, th, BYPASS_CAPA, 1);
660                 if (rc) {
661                         /* try to insert the old one */
662                         rc = dt_insert(env, obj, (struct dt_rec *)&qti->qti_rec,
663                                        key, th, BYPASS_CAPA, 1);
664                         LASSERTF(rc == 0, "failed to insert record in quota "
665                                  "index "DFID,
666                                  PFID(lu_object_fid(&obj->do_lu)));
667                         GOTO(out, rc);
668                 }
669         }
670
671         if (flags != 0) {
672                 LASSERT(ver);
673                 if (flags & LQUOTA_BUMP_VER) {
674                         /* caller wants to bump the version, let's first read
675                          * it */
676                         *ver = dt_version_get(env, obj);
677                         (*ver)++;
678                 } else {
679                         LASSERT(flags & LQUOTA_SET_VER);
680                 }
681                 dt_version_set(env, obj, *ver, th);
682         }
683
684         EXIT;
685 out:
686         dt_write_unlock(env, obj);
687         return rc;
688 }
689
690 /*
691  * Update version of an index file
692  *
693  * \param env - is the environment passed by the caller
694  * \param dev - is the backend dt device storing the index file
695  * \param obj - is the on-disk index that should be updated
696  * \param ver - is the new version
697  */
698 int lquota_disk_update_ver(const struct lu_env *env, struct dt_device *dev,
699                            struct dt_object *obj, __u64 ver)
700 {
701         struct thandle  *th;
702         int              rc;
703         ENTRY;
704
705         th = dt_trans_create(env, dev);
706         if (IS_ERR(th))
707                 RETURN(PTR_ERR(th));
708
709         rc = dt_declare_version_set(env, obj, th);
710         if (rc)
711                 GOTO(out, rc);
712
713         rc = dt_trans_start_local(env, dev, th);
714         if (rc)
715                 GOTO(out, rc);
716         th->th_sync = 1;
717
718         dt_version_set(env, obj, ver, th);
719         EXIT;
720 out:
721         dt_trans_stop(env, dev, th);
722         return rc;
723 }