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