1 /*
2  * Copyright (C) 2016 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 <fuse_lowlevel.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <pthread.h>
29 
30 #include "massert.h"
31 #include "datapack.h"
32 
33 typedef struct _dircache {
34 	struct fuse_ctx ctx;
35 	uint32_t parent;
36 	const uint8_t *dbuff;
37 	uint32_t dsize;
38 	uint32_t hashsize;
39 	const uint8_t **namehashtab;
40 	const uint8_t **inodehashtab;
41 	struct _dircache *next,**prev;
42 } dircache;
43 
44 static dircache *head;
45 static pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;
46 
dcache_hash(const uint8_t * name,uint8_t nleng)47 static inline uint32_t dcache_hash(const uint8_t *name,uint8_t nleng) {
48 	uint32_t hash=5381;
49 	while (nleng>0) {
50 		hash = ((hash<<5)+hash)^(*name);
51 		name++;
52 		nleng--;
53 	}
54 	return hash;
55 }
56 
dcache_elemcount(const uint8_t * dbuff,uint32_t dsize)57 uint32_t dcache_elemcount(const uint8_t *dbuff,uint32_t dsize) {
58 	const uint8_t *ptr,*eptr;
59 	uint8_t enleng;
60 	uint32_t ret;
61 	ptr = dbuff;
62 	eptr = dbuff+dsize;
63 	ret=0;
64 	while (ptr<eptr) {
65 		enleng = *ptr;
66 		if (ptr+enleng+40<=eptr) {
67 			ret++;
68 		}
69 		ptr+=enleng+40;
70 	}
71 	return ret;
72 }
73 
dcache_calchashsize(dircache * d)74 static inline void dcache_calchashsize(dircache *d) {
75 	uint32_t cnt = dcache_elemcount(d->dbuff,d->dsize);
76 	d->hashsize = 1;
77 	cnt = (cnt*3)/2;
78 	while (cnt) {
79 		d->hashsize<<=1;
80 		cnt>>=1;
81 	}
82 }
83 
dcache_makenamehash(dircache * d)84 void dcache_makenamehash(dircache *d) {
85 	const uint8_t *ptr,*eptr;
86 	uint8_t enleng;
87 	uint32_t hash,disp;
88 	uint32_t hashmask;
89 
90 	if (d->hashsize==0) {
91 		dcache_calchashsize(d);
92 	}
93 	hashmask = d->hashsize-1;
94 	d->namehashtab = malloc(sizeof(uint8_t*)*d->hashsize);
95 	memset(d->namehashtab,0,sizeof(uint8_t*)*d->hashsize);
96 
97 	ptr = d->dbuff;
98 	eptr = d->dbuff+d->dsize;
99 	while (ptr<eptr) {
100 		enleng = *ptr;
101 		if (ptr+enleng+40<=eptr) {
102 			hash = dcache_hash(ptr+1,enleng);
103 			disp = ((hash*0x53B23891)&hashmask)|1;
104 			while (d->namehashtab[hash&hashmask]) {
105 				hash+=disp;
106 			}
107 			d->namehashtab[hash&hashmask]=ptr;
108 		}
109 		ptr+=enleng+40;
110 	}
111 }
112 
dcache_makeinodehash(dircache * d)113 void dcache_makeinodehash(dircache *d) {
114 	const uint8_t *iptr,*ptr,*eptr;
115 	uint8_t enleng;
116 	uint32_t hash,disp;
117 	uint32_t hashmask;
118 
119 	if (d->hashsize==0) {
120 		dcache_calchashsize(d);
121 	}
122 	hashmask = d->hashsize-1;
123 	d->inodehashtab = malloc(sizeof(uint8_t*)*d->hashsize);
124 	memset(d->inodehashtab,0,sizeof(uint8_t*)*d->hashsize);
125 
126 	ptr = d->dbuff;
127 	eptr = d->dbuff+d->dsize;
128 	while (ptr<eptr) {
129 		enleng = *ptr;
130 		if (ptr+enleng+40<=eptr) {
131 			iptr = ptr+1+enleng;
132 			hash = get32bit(&iptr);
133 			disp = ((hash*0x53B23891)&hashmask)|1;
134 			hash *= 0xB28E457D;
135 			while (d->inodehashtab[hash&hashmask]) {
136 				hash+=disp;
137 			}
138 			d->inodehashtab[hash&hashmask]=ptr+1+enleng;
139 		}
140 		ptr+=enleng+40;
141 	}
142 }
143 
dcache_new(const struct fuse_ctx * ctx,uint32_t parent,const uint8_t * dbuff,uint32_t dsize)144 void* dcache_new(const struct fuse_ctx *ctx,uint32_t parent,const uint8_t *dbuff,uint32_t dsize) {
145 	dircache *d;
146 	d = malloc(sizeof(dircache));
147 	d->ctx.pid = ctx->pid;
148 	d->ctx.uid = ctx->uid;
149 	d->ctx.gid = ctx->gid;
150 	d->parent = parent;
151 	d->dbuff = dbuff;
152 	d->dsize = dsize;
153 	d->hashsize = 0;
154 	d->namehashtab = NULL;
155 	d->inodehashtab = NULL;
156 	zassert(pthread_mutex_lock(&glock));
157 	if (head) {
158 		head->prev = &(d->next);
159 	}
160 	d->next = head;
161 	d->prev = &head;
162 	head = d;
163 	zassert(pthread_mutex_unlock(&glock));
164 	return d;
165 }
166 
dcache_release(void * r)167 void dcache_release(void *r) {
168 	dircache *d = (dircache*)r;
169 	zassert(pthread_mutex_lock(&glock));
170 	if (d->next) {
171 		d->next->prev = d->prev;
172 	}
173 	*(d->prev) = d->next;
174 	zassert(pthread_mutex_unlock(&glock));
175 	if (d->namehashtab) {
176 		free(d->namehashtab);
177 	}
178 	if (d->inodehashtab) {
179 		free(d->inodehashtab);
180 	}
181 	free(d);
182 }
183 
184 /*
185 static inline uint8_t dcache_namesearch(const uint8_t *dbuff,uint32_t dsize,uint8_t nleng,const uint8_t *name,uint32_t *inode,uint8_t attr[35]) {
186 	const uint8_t *ptr,*eptr;
187 	uint8_t enleng;
188 	ptr = dbuff;
189 	eptr = dbuff+dsize;
190 	while (ptr<eptr) {
191 		enleng = *ptr;
192 		if (ptr+enleng+40<=eptr && enleng==nleng && memcmp(ptr+1,name,enleng)==0) {
193 			ptr+=1+enleng;
194 			*inode = get32bit(&ptr);
195 			memcpy(attr,ptr,35);
196 			return 1;
197 		}
198 		ptr+=enleng+40;
199 	}
200 	return 0;
201 }
202 
203 static inline uint8_t dcache_inodesearch(const uint8_t *dbuff,uint32_t dsize,uint32_t inode,uint8_t attr[35]) {
204 	const uint8_t *ptr,*eptr;
205 	uint8_t enleng;
206 	ptr = dbuff;
207 	eptr = dbuff+dsize;
208 	while (ptr<eptr) {
209 		enleng = *ptr;
210 		if (ptr+enleng+40<=eptr) {
211 			ptr+=1+enleng;
212 			if (inode==get32bit(&ptr)) {
213 				memcpy(attr,ptr,35);
214 				return 1;
215 			} else {
216 				ptr+=35;
217 			}
218 		} else {
219 			return 0;
220 		}
221 	}
222 	return 0;
223 }
224 */
225 
dcache_namehash_invalidate(dircache * d,uint8_t nleng,const uint8_t * name)226 static inline void dcache_namehash_invalidate(dircache *d,uint8_t nleng,const uint8_t *name) {
227 	uint32_t hash,disp,hashmask;
228 	const uint8_t *ptr;
229 
230 	if (d->namehashtab==NULL) {
231 		dcache_makenamehash(d);
232 	}
233 	hashmask = d->hashsize-1;
234 	hash = dcache_hash(name,nleng);
235 	disp = ((hash*0x53B23891)&hashmask)|1;
236 	while ((ptr=d->namehashtab[hash&hashmask])) {
237 		if (*ptr==nleng && memcmp(ptr+1,name,nleng)==0) {
238 			ptr+=1+nleng;
239 			memset((uint8_t*)ptr,0,sizeof(uint32_t)+35);
240 			return;
241 		}
242 		hash+=disp;
243 	}
244 }
245 
dcache_namehash_get(dircache * d,uint8_t nleng,const uint8_t * name,uint32_t * inode,uint8_t attr[35])246 static inline uint8_t dcache_namehash_get(dircache *d,uint8_t nleng,const uint8_t *name,uint32_t *inode,uint8_t attr[35]) {
247 	uint32_t hash,disp,hashmask;
248 	const uint8_t *ptr;
249 
250 	if (d->namehashtab==NULL) {
251 		dcache_makenamehash(d);
252 	}
253 	hashmask = d->hashsize-1;
254 	hash = dcache_hash(name,nleng);
255 	disp = ((hash*0x53B23891)&hashmask)|1;
256 	while ((ptr=d->namehashtab[hash&hashmask])) {
257 		if (*ptr==nleng && memcmp(ptr+1,name,nleng)==0) {
258 			ptr+=1+nleng;
259 			*inode = get32bit(&ptr);
260 			if (*ptr) { // are attributes valid ?
261 				memcpy(attr,ptr,35);
262 				return 1;
263 			} else {
264 				return 0;
265 			}
266 		}
267 		hash+=disp;
268 	}
269 	return 0;
270 }
271 
dcache_inodehash_get(dircache * d,uint32_t inode,uint8_t attr[35])272 static inline uint8_t dcache_inodehash_get(dircache *d,uint32_t inode,uint8_t attr[35]) {
273 	uint32_t hash,disp,hashmask;
274 	const uint8_t *ptr;
275 
276 	if (d->inodehashtab==NULL) {
277 		dcache_makeinodehash(d);
278 	}
279 	hashmask = d->hashsize-1;
280 	hash = inode*0xB28E457D;
281 	disp = ((inode*0x53B23891)&hashmask)|1;
282 	while ((ptr=d->inodehashtab[hash&hashmask])) {
283 		if (inode==get32bit(&ptr)) {
284 			if (*ptr) { // are attributes valid ?
285 				memcpy(attr,ptr,35);
286 				return 1;
287 			} else {
288 				return 0;
289 			}
290 		}
291 		hash+=disp;
292 	}
293 	return 0;
294 }
295 
dcache_inodehash_set(dircache * d,uint32_t inode,const uint8_t attr[35])296 static inline uint8_t dcache_inodehash_set(dircache *d,uint32_t inode,const uint8_t attr[35]) {
297 	uint32_t hash,disp,hashmask;
298 	const uint8_t *ptr;
299 
300 	if (d->inodehashtab==NULL) {
301 		dcache_makeinodehash(d);
302 	}
303 	hashmask = d->hashsize-1;
304 	hash = inode*0xB28E457D;
305 	disp = ((inode*0x53B23891)&hashmask)|1;
306 	while ((ptr=d->inodehashtab[hash&hashmask])) {
307 		if (inode==get32bit(&ptr)) {
308 			memcpy((uint8_t*)ptr,attr,35);
309 			return 1;
310 		}
311 		hash+=disp;
312 	}
313 	return 0;
314 }
315 
dcache_inodehash_invalidate_attr(dircache * d,uint32_t inode)316 static inline uint8_t dcache_inodehash_invalidate_attr(dircache *d,uint32_t inode) {
317 	uint32_t hash,disp,hashmask;
318 	const uint8_t *ptr;
319 
320 	if (d->inodehashtab==NULL) {
321 		dcache_makeinodehash(d);
322 	}
323 	hashmask = d->hashsize-1;
324 	hash = inode*0xB28E457D;
325 	disp = ((inode*0x53B23891)&hashmask)|1;
326 	while ((ptr=d->inodehashtab[hash&hashmask])) {
327 		if (inode==get32bit(&ptr)) {
328 			memset((uint8_t*)ptr,0,35);
329 			return 1;
330 		}
331 		hash+=disp;
332 	}
333 	return 0;
334 }
335 
dcache_lookup(const struct fuse_ctx * ctx,uint32_t parent,uint8_t nleng,const uint8_t * name,uint32_t * inode,uint8_t attr[35])336 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[35]) {
337 	dircache *d;
338 	zassert(pthread_mutex_lock(&glock));
339 	for (d=head ; d ; d=d->next) {
340 		if (parent==d->parent && ctx->pid==d->ctx.pid && ctx->uid==d->ctx.uid && ctx->gid==d->ctx.gid) {
341 			if (dcache_namehash_get(d,nleng,name,inode,attr)) {
342 				zassert(pthread_mutex_unlock(&glock));
343 				return 1;
344 			}
345 		}
346 	}
347 	zassert(pthread_mutex_unlock(&glock));
348 	return 0;
349 }
350 
dcache_getattr(const struct fuse_ctx * ctx,uint32_t inode,uint8_t attr[35])351 uint8_t dcache_getattr(const struct fuse_ctx *ctx,uint32_t inode,uint8_t attr[35]) {
352 	dircache *d;
353 	zassert(pthread_mutex_lock(&glock));
354 	for (d=head ; d ; d=d->next) {
355 		if (ctx->pid==d->ctx.pid && ctx->uid==d->ctx.uid && ctx->gid==d->ctx.gid) {
356 			if (dcache_inodehash_get(d,inode,attr)) {
357 				zassert(pthread_mutex_unlock(&glock));
358 				return 1;
359 			}
360 		}
361 	}
362 	zassert(pthread_mutex_unlock(&glock));
363 	return 0;
364 }
365 
dcache_setattr(uint32_t inode,const uint8_t attr[35])366 void dcache_setattr(uint32_t inode,const uint8_t attr[35]) {
367 	dircache *d;
368 	zassert(pthread_mutex_lock(&glock));
369 	for (d=head ; d ; d=d->next) {
370 		dcache_inodehash_set(d,inode,attr);
371 	}
372 	zassert(pthread_mutex_unlock(&glock));
373 }
374 
dcache_invalidate_attr(uint32_t inode)375 void dcache_invalidate_attr(uint32_t inode) {
376 	dircache *d;
377 	zassert(pthread_mutex_lock(&glock));
378 	for (d=head ; d ; d=d->next) {
379 		dcache_inodehash_invalidate_attr(d,inode);
380 	}
381 	zassert(pthread_mutex_unlock(&glock));
382 }
383 
dcache_invalidate_name(const struct fuse_ctx * ctx,uint32_t parent,uint8_t nleng,const uint8_t * name)384 void dcache_invalidate_name(const struct fuse_ctx *ctx,uint32_t parent,uint8_t nleng,const uint8_t *name) {
385 	dircache *d;
386 	zassert(pthread_mutex_lock(&glock));
387 	for (d=head ; d ; d=d->next) {
388 		if (parent==d->parent && ctx->pid==d->ctx.pid && ctx->uid==d->ctx.uid && ctx->gid==d->ctx.gid) {
389 			dcache_namehash_invalidate(d,nleng,name);
390 		}
391 	}
392 	zassert(pthread_mutex_unlock(&glock));
393 }
394