1/*
2 * Copyright 2016, NICTA
3 *
4 * This software may be distributed and modified according to the terms of
5 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
6 * See "LICENSE_GPLv2.txt" for details.
7 *
8 * @TAG(NICTA_GPL)
9 */
10
11#include <bilbyfs.h>
12
13struct obj_dentry *dentarr_first_dentry(struct obj_dentarr *dentarr)
14{
15        if (le32_to_cpu(dentarr->nb_dentry) == 0)
16                return NULL;
17        return (void *)dentarr + BILBYFS_DENTARR_SZ;
18}
19
20static struct obj_dentry *dentarr_end(struct obj_dentarr *dentarr)
21{
22        return ((void *)dentarr + obj_dentarr_size(dentarr));
23}
24
25struct obj_dentry *dentarr_next_dentry(struct obj_dentarr *dentarr,
26                                       struct obj_dentry *dentry)
27{
28        struct obj_dentry *next_de;
29
30        next_de = (void *) dentry + obj_dentry_size(dentry);
31        if (next_de == dentarr_end(dentarr))
32                return NULL;
33        return next_de;
34}
35
36int dentarr_check_empty(struct bilbyfs_info *bi, struct obj_dentarr *dentarr)
37{
38        return (!dentarr_first_dentry(dentarr) ? 0 : -ENOTEMPTY);
39}
40
41int dentarr_add_dentry(struct bilbyfs_info *bi, struct obj_dentarr *dentarr,
42                       struct inode *inode, const char *name)
43{
44        struct obj_dentry *de;
45        int sz_change;
46        int nb_dentry;
47        obj_id id;
48        int len;
49
50        sz_change = obj_dentry_size_from_nm(name);
51        id = le64_to_cpu(dentarr->id);
52        nb_dentry = le32_to_cpu(dentarr->nb_dentry) + 1;
53        len = obj_dentarr_size(dentarr);
54        de = dentarr_end(dentarr);
55
56        pack_obj_dentry(de, inode, name);
57        pack_obj_dentarr(dentarr, id, nb_dentry, len + sz_change);
58        return sz_change;
59}
60
61int dentarr_del_dentry(struct bilbyfs_info *bi, struct obj_dentarr *dentarr,
62                       const char *name)
63{
64        struct obj_dentarr *copy_dentarr;
65        struct obj_dentry *de;
66        struct obj_dentry *copy_de;
67        int sz_change;
68        int nb_dentry;
69        obj_id id;
70        int delen;
71        int len;
72
73        sz_change = obj_dentry_size_from_nm(name);
74        id = le64_to_cpu(dentarr->id);
75        nb_dentry = le32_to_cpu(dentarr->nb_dentry);
76        len = obj_dentarr_size(dentarr);
77
78        copy_dentarr = kmalloc(len);
79        if (!copy_dentarr)
80                return -ENOMEM;
81        memcpy(copy_dentarr, dentarr, len);
82
83        copy_de = dentarr_lookup_nm(bi, copy_dentarr, name);
84        de = dentarr_lookup_nm(bi, dentarr, name);
85        if (nb_dentry <= 0 || !copy_de) {
86                kfree(copy_dentarr);
87                return 0;
88        }
89
90        copy_de = dentarr_next_dentry(copy_dentarr, copy_de);
91        while (copy_de) {
92                delen = obj_dentry_size(de);
93                memcpy(de, copy_de, delen);
94                de = (void *) de + delen;
95                copy_de = dentarr_next_dentry(copy_dentarr, copy_de);
96        }
97        kfree(copy_dentarr);
98
99        nb_dentry--;
100        if (nb_dentry == 0) {
101                memset(dentarr, 0, len);
102                pack_obj_del(dentarr, id);
103        } else {
104                pack_obj_dentarr(dentarr, id, nb_dentry, len - sz_change);
105        }
106        return sz_change;
107}
108
109struct obj_dentry *dentarr_lookup_nm(struct bilbyfs_info *bi,
110                                     struct obj_dentarr *dentarr,
111                                     const char *name)
112{
113        struct obj_dentry *de;
114
115        de = dentarr_first_dentry(dentarr);
116        while (de) {
117                if (!strcmp(de->name, name))
118                        return de;
119                de = dentarr_next_dentry(dentarr, de);
120        }
121        return ERR_PTR(-ENOENT);
122}
123
124struct obj_dentarr *dentarr_read(struct bilbyfs_info *bi, obj_id id)
125{
126        struct obj_dentarr *dentarr;
127        int err;
128        /* We need to allocate enough to be able to add an entry */
129        int size = BILBYFS_MAX_DENTARR_SZ;
130
131        bilbyfs_debug("[S] dentarr_read(0x%llx)\n", id);
132        dentarr = kmalloc(size);
133        if (!dentarr)
134                return ERR_PTR(-ENOMEM);
135
136        err = ostore_read_obj(bi, id, dentarr, size);
137        if (err) {
138                kfree(dentarr);
139                return ERR_PTR(err);
140        }
141        /* Sanity checking */
142        if (le32_to_cpu(dentarr->nb_dentry) > BILBYFS_MAX_DENTARR_ENTRIES ||
143            le32_to_cpu(dentarr->size) > BILBYFS_MAX_DENTARR_SZ) {
144                kfree(dentarr);
145                bilbyfs_err("dentarr_read: lookup err nb_dentry=%d, size=%d\n", le32_to_cpu(dentarr->nb_dentry), le32_to_cpu(dentarr->size));
146                return ERR_PTR(-EINVAL);
147        }
148        return dentarr;
149}
150
151struct obj_dentarr *dentarr_read_or_create(struct bilbyfs_info *bi, obj_id id)
152{
153        struct obj_dentarr *dentarr;
154
155        dentarr = dentarr_read(bi, id);
156        if (IS_ERR(dentarr)) {
157                if (PTR_ERR(dentarr) != -ENOENT)
158                        return dentarr;
159                dentarr = kmalloc(BILBYFS_DENTARR_SZ + BILBYFS_MAX_DENTRY_SZ);
160                if (!dentarr)
161                        return ERR_PTR(-ENOMEM);
162                pack_obj_dentarr(dentarr, id, 0, BILBYFS_DENTARR_SZ);
163        }
164        return dentarr;
165}
166