1 /*
2 * Copyright (C) 2021 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3 *
4 * This file is part of MooseFS.
5 *
6 * MooseFS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 2 (only).
9 *
10 * MooseFS is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with MooseFS; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18 * or visit http://www.gnu.org/licenses/gpl-2.0.html
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "fusecommon.h"
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <pthread.h>
30
31 #include "dirattrcache.h"
32 #include "massert.h"
33 #include "datapack.h"
34
35 typedef struct _dircache {
36 struct fuse_ctx ctx;
37 uint32_t parent;
38 const uint8_t *dbuff;
39 uint32_t dsize;
40 uint32_t hashsize;
41 uint8_t attrsize;
42 const uint8_t **namehashtab;
43 const uint8_t **inodehashtab;
44 struct _dircache *next,**prev;
45 } dircache;
46
47 static dircache *head;
48 static pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;
49
dcache_hash(const uint8_t * name,uint8_t nleng)50 static inline uint32_t dcache_hash(const uint8_t *name,uint8_t nleng) {
51 uint32_t hash=5381;
52 while (nleng>0) {
53 hash = ((hash<<5)+hash)^(*name);
54 name++;
55 nleng--;
56 }
57 return hash;
58 }
59
dcache_elemcount(const uint8_t * dbuff,uint32_t dsize,uint8_t attrsize)60 static inline uint32_t dcache_elemcount(const uint8_t *dbuff,uint32_t dsize,uint8_t attrsize) {
61 const uint8_t *ptr,*eptr;
62 uint16_t enleng;
63 uint32_t ret;
64 ptr = dbuff;
65 eptr = dbuff+dsize;
66 ret=0;
67 while (ptr<eptr) {
68 enleng = *ptr;
69 if (ptr+enleng+5U+attrsize<=eptr) {
70 ret++;
71 }
72 ptr+=enleng+5U+attrsize;
73 }
74 return ret;
75 }
76
dcache_calchashsize(dircache * d)77 static inline void dcache_calchashsize(dircache *d) {
78 uint32_t cnt = dcache_elemcount(d->dbuff,d->dsize,d->attrsize);
79 d->hashsize = 1;
80 cnt = (cnt*3)/2;
81 while (cnt) {
82 d->hashsize<<=1;
83 cnt>>=1;
84 }
85 }
86
dcache_makenamehash(dircache * d)87 void dcache_makenamehash(dircache *d) {
88 const uint8_t *ptr,*eptr;
89 uint16_t enleng;
90 uint32_t hash,disp;
91 uint32_t hashmask;
92
93 if (d->hashsize==0) {
94 dcache_calchashsize(d);
95 }
96 hashmask = d->hashsize-1;
97 d->namehashtab = malloc(sizeof(uint8_t*)*d->hashsize);
98 memset(d->namehashtab,0,sizeof(uint8_t*)*d->hashsize);
99
100 ptr = d->dbuff;
101 eptr = d->dbuff+d->dsize;
102 while (ptr<eptr) {
103 enleng = *ptr;
104 if (ptr+enleng+5U+d->attrsize<=eptr) {
105 hash = dcache_hash(ptr+1,enleng);
106 disp = ((hash*0x53B23891)&hashmask)|1;
107 while (d->namehashtab[hash&hashmask]) {
108 hash+=disp;
109 }
110 d->namehashtab[hash&hashmask]=ptr;
111 }
112 ptr+=enleng+5U+d->attrsize;
113 }
114 }
115
dcache_makeinodehash(dircache * d)116 void dcache_makeinodehash(dircache *d) {
117 const uint8_t *iptr,*ptr,*eptr;
118 uint16_t enleng;
119 uint32_t hash,disp;
120 uint32_t hashmask;
121
122 if (d->hashsize==0) {
123 dcache_calchashsize(d);
124 }
125 hashmask = d->hashsize-1;
126 d->inodehashtab = malloc(sizeof(uint8_t*)*d->hashsize);
127 memset(d->inodehashtab,0,sizeof(uint8_t*)*d->hashsize);
128
129 ptr = d->dbuff;
130 eptr = d->dbuff+d->dsize;
131 while (ptr<eptr) {
132 enleng = *ptr;
133 if (ptr+enleng+5U+d->attrsize<=eptr) {
134 iptr = ptr+1U+enleng;
135 hash = get32bit(&iptr);
136 disp = ((hash*0x53B23891)&hashmask)|1;
137 hash *= 0xB28E457D;
138 while (d->inodehashtab[hash&hashmask]) {
139 hash+=disp;
140 }
141 d->inodehashtab[hash&hashmask]=ptr+1U+enleng;
142 }
143 ptr+=enleng+5U+d->attrsize;
144 }
145 }
146
dcache_new(const struct fuse_ctx * ctx,uint32_t parent,const uint8_t * dbuff,uint32_t dsize,uint8_t attrsize)147 void* dcache_new(const struct fuse_ctx *ctx,uint32_t parent,const uint8_t *dbuff,uint32_t dsize,uint8_t attrsize) {
148 dircache *d;
149 d = malloc(sizeof(dircache));
150 d->ctx.pid = ctx->pid;
151 d->ctx.uid = ctx->uid;
152 d->ctx.gid = ctx->gid;
153 d->parent = parent;
154 d->dbuff = dbuff;
155 d->dsize = dsize;
156 d->attrsize = attrsize;
157 d->hashsize = 0;
158 d->namehashtab = NULL;
159 d->inodehashtab = NULL;
160 zassert(pthread_mutex_lock(&glock));
161 if (head) {
162 head->prev = &(d->next);
163 }
164 d->next = head;
165 d->prev = &head;
166 head = d;
167 zassert(pthread_mutex_unlock(&glock));
168 return d;
169 }
170
dcache_release(void * r)171 void dcache_release(void *r) {
172 dircache *d = (dircache*)r;
173 zassert(pthread_mutex_lock(&glock));
174 if (d->next) {
175 d->next->prev = d->prev;
176 }
177 *(d->prev) = d->next;
178 zassert(pthread_mutex_unlock(&glock));
179 if (d->namehashtab) {
180 free(d->namehashtab);
181 }
182 if (d->inodehashtab) {
183 free(d->inodehashtab);
184 }
185 free(d);
186 }
187
dcache_namehash_invalidate(dircache * d,uint8_t nleng,const uint8_t * name)188 static inline void dcache_namehash_invalidate(dircache *d,uint8_t nleng,const uint8_t *name) {
189 uint32_t hash,disp,hashmask;
190 const uint8_t *ptr;
191
192 if (d->namehashtab==NULL) {
193 dcache_makenamehash(d);
194 }
195 hashmask = d->hashsize-1;
196 hash = dcache_hash(name,nleng);
197 disp = ((hash*0x53B23891)&hashmask)|1;
198 while ((ptr=d->namehashtab[hash&hashmask])) {
199 if (*ptr==nleng && memcmp(ptr+1,name,nleng)==0) {
200 ptr+=1U+(uint16_t)nleng;
201 memset((uint8_t*)ptr,0,sizeof(uint32_t)+d->attrsize);
202 return;
203 }
204 hash+=disp;
205 }
206 }
207
dcache_namehash_get(dircache * d,uint8_t nleng,const uint8_t * name,uint32_t * inode,uint8_t attr[ATTR_RECORD_SIZE])208 static inline uint8_t dcache_namehash_get(dircache *d,uint8_t nleng,const uint8_t *name,uint32_t *inode,uint8_t attr[ATTR_RECORD_SIZE]) {
209 uint32_t hash,disp,hashmask;
210 const uint8_t *ptr;
211
212 if (d->namehashtab==NULL) {
213 dcache_makenamehash(d);
214 }
215 hashmask = d->hashsize-1;
216 hash = dcache_hash(name,nleng);
217 disp = ((hash*0x53B23891)&hashmask)|1;
218 while ((ptr=d->namehashtab[hash&hashmask])) {
219 if (*ptr==nleng && memcmp(ptr+1,name,nleng)==0) {
220 ptr+=1U+(uint16_t)nleng;
221 *inode = get32bit(&ptr);
222 if (*ptr) { // are attributes valid ?
223 if (d->attrsize>=ATTR_RECORD_SIZE) {
224 memcpy(attr,ptr,ATTR_RECORD_SIZE);
225 } else {
226 memcpy(attr,ptr,d->attrsize);
227 memset(attr+d->attrsize,0,ATTR_RECORD_SIZE-d->attrsize);
228 }
229 return 1;
230 } else {
231 return 0;
232 }
233 }
234 hash+=disp;
235 }
236 return 0;
237 }
238
dcache_inodehash_get(dircache * d,uint32_t inode,uint8_t attr[ATTR_RECORD_SIZE])239 static inline uint8_t dcache_inodehash_get(dircache *d,uint32_t inode,uint8_t attr[ATTR_RECORD_SIZE]) {
240 uint32_t hash,disp,hashmask;
241 const uint8_t *ptr;
242
243 if (d->inodehashtab==NULL) {
244 dcache_makeinodehash(d);
245 }
246 hashmask = d->hashsize-1;
247 hash = inode*0xB28E457D;
248 disp = ((inode*0x53B23891)&hashmask)|1;
249 while ((ptr=d->inodehashtab[hash&hashmask])) {
250 if (inode==get32bit(&ptr)) {
251 if (*ptr) { // are attributes valid ?
252 if (d->attrsize>=ATTR_RECORD_SIZE) {
253 memcpy(attr,ptr,ATTR_RECORD_SIZE);
254 } else {
255 memcpy(attr,ptr,d->attrsize);
256 memset(attr+d->attrsize,0,ATTR_RECORD_SIZE-d->attrsize);
257 }
258 return 1;
259 } else {
260 return 0;
261 }
262 }
263 hash+=disp;
264 }
265 return 0;
266 }
267
dcache_inodehash_set(dircache * d,uint32_t inode,const uint8_t attr[ATTR_RECORD_SIZE])268 static inline uint8_t dcache_inodehash_set(dircache *d,uint32_t inode,const uint8_t attr[ATTR_RECORD_SIZE]) {
269 uint32_t hash,disp,hashmask;
270 const uint8_t *ptr;
271
272 if (d->inodehashtab==NULL) {
273 dcache_makeinodehash(d);
274 }
275 hashmask = d->hashsize-1;
276 hash = inode*0xB28E457D;
277 disp = ((inode*0x53B23891)&hashmask)|1;
278 while ((ptr=d->inodehashtab[hash&hashmask])) {
279 if (inode==get32bit(&ptr)) {
280 if (d->attrsize<ATTR_RECORD_SIZE) {
281 memcpy((uint8_t*)ptr,attr,d->attrsize);
282 } else {
283 memcpy((uint8_t*)ptr,attr,ATTR_RECORD_SIZE);
284 }
285 return 1;
286 }
287 hash+=disp;
288 }
289 return 0;
290 }
291
dcache_inodehash_invalidate_attr(dircache * d,uint32_t inode)292 static inline uint8_t dcache_inodehash_invalidate_attr(dircache *d,uint32_t inode) {
293 uint32_t hash,disp,hashmask;
294 const uint8_t *ptr;
295
296 if (d->inodehashtab==NULL) {
297 dcache_makeinodehash(d);
298 }
299 hashmask = d->hashsize-1;
300 hash = inode*0xB28E457D;
301 disp = ((inode*0x53B23891)&hashmask)|1;
302 while ((ptr=d->inodehashtab[hash&hashmask])) {
303 if (inode==get32bit(&ptr)) {
304 memset((uint8_t*)ptr,0,d->attrsize);
305 return 1;
306 }
307 hash+=disp;
308 }
309 return 0;
310 }
311
dcache_lookup(const struct fuse_ctx * ctx,uint32_t parent,uint8_t nleng,const uint8_t * name,uint32_t * inode,uint8_t attr[ATTR_RECORD_SIZE])312 uint8_t dcache_lookup(const struct fuse_ctx *ctx,uint32_t parent,uint8_t nleng,const uint8_t *name,uint32_t *inode,uint8_t attr[ATTR_RECORD_SIZE]) {
313 dircache *d;
314 zassert(pthread_mutex_lock(&glock));
315 for (d=head ; d ; d=d->next) {
316 if (parent==d->parent && ctx->pid==d->ctx.pid && ctx->uid==d->ctx.uid && ctx->gid==d->ctx.gid) {
317 if (dcache_namehash_get(d,nleng,name,inode,attr)) {
318 zassert(pthread_mutex_unlock(&glock));
319 return 1;
320 }
321 }
322 }
323 zassert(pthread_mutex_unlock(&glock));
324 return 0;
325 }
326
dcache_getattr(const struct fuse_ctx * ctx,uint32_t inode,uint8_t attr[ATTR_RECORD_SIZE])327 uint8_t dcache_getattr(const struct fuse_ctx *ctx,uint32_t inode,uint8_t attr[ATTR_RECORD_SIZE]) {
328 dircache *d;
329 zassert(pthread_mutex_lock(&glock));
330 for (d=head ; d ; d=d->next) {
331 if (ctx->pid==d->ctx.pid && ctx->uid==d->ctx.uid && ctx->gid==d->ctx.gid) {
332 if (dcache_inodehash_get(d,inode,attr)) {
333 zassert(pthread_mutex_unlock(&glock));
334 return 1;
335 }
336 }
337 }
338 zassert(pthread_mutex_unlock(&glock));
339 return 0;
340 }
341
dcache_setattr(uint32_t inode,const uint8_t attr[ATTR_RECORD_SIZE])342 void dcache_setattr(uint32_t inode,const uint8_t attr[ATTR_RECORD_SIZE]) {
343 dircache *d;
344 zassert(pthread_mutex_lock(&glock));
345 for (d=head ; d ; d=d->next) {
346 dcache_inodehash_set(d,inode,attr);
347 }
348 zassert(pthread_mutex_unlock(&glock));
349 }
350
dcache_invalidate_attr(uint32_t inode)351 void dcache_invalidate_attr(uint32_t inode) {
352 dircache *d;
353 zassert(pthread_mutex_lock(&glock));
354 for (d=head ; d ; d=d->next) {
355 dcache_inodehash_invalidate_attr(d,inode);
356 }
357 zassert(pthread_mutex_unlock(&glock));
358 }
359
dcache_invalidate_name(const struct fuse_ctx * ctx,uint32_t parent,uint8_t nleng,const uint8_t * name)360 void dcache_invalidate_name(const struct fuse_ctx *ctx,uint32_t parent,uint8_t nleng,const uint8_t *name) {
361 dircache *d;
362 zassert(pthread_mutex_lock(&glock));
363 for (d=head ; d ; d=d->next) {
364 if (parent==d->parent && ctx->pid==d->ctx.pid && ctx->uid==d->ctx.uid && ctx->gid==d->ctx.gid) {
365 dcache_namehash_invalidate(d,nleng,name);
366 }
367 }
368 zassert(pthread_mutex_unlock(&glock));
369 }
370