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 <stdlib.h>
26 #include <string.h>
27 #include <inttypes.h>
28 
29 #include "MFSCommunication.h"
30 #include "datapack.h"
31 #include "slogger.h"
32 #include "massert.h"
33 #include "filesystem.h"
34 
35 #include "glue.h"
36 
37 typedef struct acl_entry {
38 	uint32_t id;
39 	uint16_t perm;
40 } acl_entry;
41 
42 typedef struct acl_node {
43 	uint32_t inode;
44 	uint8_t acltype;
45 	uint16_t userperm;
46 	uint16_t groupperm;
47 	uint16_t otherperm;
48 	uint16_t mask;
49 	uint16_t namedusers;
50 	uint16_t namedgroups;
51 	acl_entry *acltab;
52 	struct acl_node *next;
53 } acl_node;
54 
55 #define LOHASH_BITS 20
56 #define ENTRY_TYPE acl_node
57 #define GLUE_FN_NAME_PREFIX(Y) GLUE(posix_acl_xxx,Y)
58 #define HASH_ARGS_TYPE_LIST uint32_t inode,uint8_t acltype
59 #define HASH_ARGS_LIST inode,acltype
60 #define GLUE_HASH_TAB_PREFIX(Y) GLUE(pacl,Y)
61 
GLUE_FN_NAME_PREFIX(_cmp)62 static inline int GLUE_FN_NAME_PREFIX(_cmp)(ENTRY_TYPE *e,HASH_ARGS_TYPE_LIST) {
63 	return (e->inode==inode && e->acltype==acltype);
64 }
65 
GLUE_FN_NAME_PREFIX(_hash)66 static inline uint32_t GLUE_FN_NAME_PREFIX(_hash)(HASH_ARGS_TYPE_LIST) {
67 	return inode*0x56BF7623+acltype;
68 }
69 
GLUE_FN_NAME_PREFIX(_ehash)70 static inline uint32_t GLUE_FN_NAME_PREFIX(_ehash)(ENTRY_TYPE *e) {
71 	return GLUE_FN_NAME_PREFIX(_hash)(e->inode,e->acltype);
72 }
73 
74 #include "hash_begin.h"
75 
posix_acl_create(uint32_t inode,uint8_t acltype)76 static acl_node* posix_acl_create(uint32_t inode,uint8_t acltype) {
77 	acl_node *acn;
78 
79 	acn = malloc(sizeof(acl_node));
80 	passert(acn);
81 	acn->inode = inode;
82 	acn->acltype = acltype;
83 	acn->userperm = 0;
84 	acn->groupperm = 0;
85 	acn->otherperm = 0;
86 	acn->mask = 0;
87 	acn->namedusers = 0;
88 	acn->namedgroups = 0;
89 	acn->acltab = NULL;
90 	GLUE_FN_NAME_PREFIX(_add)(acn);
91 	return acn;
92 }
93 
posix_acl_getmode(uint32_t inode)94 uint16_t posix_acl_getmode(uint32_t inode) {
95 	acl_node *acn;
96 
97 	acn = GLUE_FN_NAME_PREFIX(_find)(inode,POSIX_ACL_ACCESS);
98 //	(acn->mask==0xFFFF) ???
99 	return ((acn->userperm & 7) << 6) | ((acn->mask & 7) << 3) | (acn->otherperm & 7);
100 }
101 
posix_acl_setmode(uint32_t inode,uint16_t mode)102 void posix_acl_setmode(uint32_t inode,uint16_t mode) {
103 	acl_node *acn;
104 
105 	acn = GLUE_FN_NAME_PREFIX(_find)(inode,POSIX_ACL_ACCESS);
106 	if (acn!=NULL) {
107 		acn->userperm &= 0xFFF8;
108 		acn->userperm |= (mode>>6)&7;
109 		acn->mask &= 0xFFF8;
110 		acn->mask |= (mode>>3)&7;
111 		acn->otherperm &= 0xFFF8;
112 		acn->otherperm |= mode&7;
113 	}
114 }
115 
posix_acl_accmode(uint32_t inode,uint32_t auid,uint32_t agids,uint32_t * agid,uint32_t fuid,uint32_t fgid)116 uint8_t posix_acl_accmode(uint32_t inode,uint32_t auid,uint32_t agids,uint32_t *agid,uint32_t fuid,uint32_t fgid) {
117 	static uint8_t modetoaccmode[8] = MODE_TO_ACCMODE;
118 	acl_node *acn;
119 	int f;
120 	uint16_t i;
121 	uint32_t j;
122 	uint8_t modemask;
123 
124 	if (auid==0) {
125 		return modetoaccmode[7];
126 	}
127 	acn = GLUE_FN_NAME_PREFIX(_find)(inode,POSIX_ACL_ACCESS);
128 	if (acn==NULL) {
129 		return modetoaccmode[0];
130 	}
131 	if (auid==fuid) {
132 		return modetoaccmode[acn->userperm & 0x7];
133 	} else {
134 		for (i=0 ; i<acn->namedusers ; i++) {
135 			if (auid==acn->acltab[i].id) {
136 				return modetoaccmode[acn->acltab[i].perm & acn->mask & 0x7];
137 			}
138 		}
139 		f = 0;
140 		modemask = 0;
141 		for (j=0 ; j<agids ; j++) {
142 			if (agid[j]==fgid) {
143 				modemask |= modetoaccmode[acn->groupperm & acn->mask & 0x7];
144 				f = 1;
145 			}
146 		}
147 		for (i=acn->namedusers ; i<acn->namedusers+acn->namedgroups ; i++) {
148 			for (j=0 ; j<agids ; j++) {
149 				if (agid[j]==acn->acltab[i].id) {
150 					modemask |= modetoaccmode[acn->acltab[i].perm & acn->mask & 0x7];
151 					f = 1;
152 				}
153 			}
154 		}
155 		if (f==1) {
156 			return modemask;
157 		}
158 		return modetoaccmode[acn->otherperm & 0x7];
159 	}
160 }
161 
posix_acl_copydefaults(uint32_t parent,uint32_t inode,uint8_t directory,uint16_t * mode)162 uint8_t posix_acl_copydefaults(uint32_t parent,uint32_t inode,uint8_t directory,uint16_t *mode) {
163 	uint16_t i,acls;
164 	uint8_t ret;
165 	acl_node *pacn;
166 	acl_node *acn;
167 
168 	ret = 0;
169 	pacn = GLUE_FN_NAME_PREFIX(_find)(parent,POSIX_ACL_DEFAULT);
170 	if (pacn==NULL) {
171 		return ret;
172 	}
173 	acls = pacn->namedusers + pacn->namedgroups;
174 	if (acls==0 && pacn->userperm<=7 && pacn->groupperm<=7 && pacn->otherperm<=7 && pacn->mask==0xFFFF) { // simple ACL as DEFAULT - just modify mode
175 		*mode &= 0xFE00 | (pacn->userperm << 6) | (pacn->groupperm << 3) | pacn->otherperm;
176 	} else {
177 		acn = GLUE_FN_NAME_PREFIX(_find)(inode,POSIX_ACL_ACCESS);
178 		if (acn==NULL) {
179 			acn = posix_acl_create(inode,POSIX_ACL_ACCESS);
180 			ret |= 1;
181 		}
182 		acn->userperm &= 0xFFF8;
183 		acn->userperm |= ((*mode)>>6)&7;
184 		acn->userperm &= pacn->userperm;
185 		acn->groupperm = pacn->groupperm;
186 		acn->otherperm &= 0xFFF8;
187 		acn->otherperm |= (*mode)&7;
188 		acn->otherperm &= pacn->otherperm;
189 		acn->mask &= 0xFFF8;
190 		acn->mask |= ((*mode)>>3)&7;
191 		acn->mask &= pacn->mask;
192 		*mode = ((*mode) & 0xFE00) | ((acn->userperm&7) << 6) | ((acn->groupperm&7) << 3) | (acn->otherperm&7); // groupperm not mask here !!!
193 		if (acn->namedusers+acn->namedgroups!=acls) {
194 			if (acn->acltab!=NULL) {
195 				free(acn->acltab);
196 			}
197 			if (acls>0) {
198 				acn->acltab = malloc(sizeof(acl_entry)*acls);
199 				passert(acn->acltab);
200 			} else {
201 				acn->acltab = NULL;
202 			}
203 		}
204 		acn->namedusers = pacn->namedusers;
205 		acn->namedgroups = pacn->namedgroups;
206 		for (i=0 ; i<acls ; i++) {
207 			acn->acltab[i].id = pacn->acltab[i].id;
208 			acn->acltab[i].perm = pacn->acltab[i].perm;
209 		}
210 	}
211 	if (directory) {
212 		acn = GLUE_FN_NAME_PREFIX(_find)(inode,POSIX_ACL_DEFAULT);
213 		if (acn==NULL) {
214 			acn = posix_acl_create(inode,POSIX_ACL_DEFAULT);
215 			ret |= 2;
216 		}
217 		acn->userperm = pacn->userperm;
218 		acn->groupperm = pacn->groupperm;
219 		acn->otherperm = pacn->otherperm;
220 		acn->mask = pacn->mask;
221 		if (acn->namedusers+acn->namedgroups!=acls) {
222 			if (acn->acltab!=NULL) {
223 				free(acn->acltab);
224 			}
225 			if (acls>0) {
226 				acn->acltab = malloc(sizeof(acl_entry)*acls);
227 				passert(acn->acltab);
228 			} else {
229 				acn->acltab = NULL;
230 			}
231 		}
232 		acn->namedusers = pacn->namedusers;
233 		acn->namedgroups = pacn->namedgroups;
234 		for (i=0 ; i<acls ; i++) {
235 			acn->acltab[i].id = pacn->acltab[i].id;
236 			acn->acltab[i].perm = pacn->acltab[i].perm;
237 		}
238 	}
239 	return ret;
240 }
241 
posix_acl_remove(uint32_t inode,uint8_t acltype)242 void posix_acl_remove(uint32_t inode,uint8_t acltype) {
243 	acl_node *acn;
244 	acn = GLUE_FN_NAME_PREFIX(_find)(inode,acltype);
245 	if (acn) {
246 		GLUE_FN_NAME_PREFIX(_delete)(acn);
247 		if (acn->acltab) {
248 			free(acn->acltab);
249 		}
250 		free(acn);
251 	}
252 }
253 
posix_acl_set(uint32_t inode,uint8_t acltype,uint16_t userperm,uint16_t groupperm,uint16_t otherperm,uint16_t mask,uint16_t namedusers,uint16_t namedgroups,const uint8_t * aclblob)254 void posix_acl_set(uint32_t inode,uint8_t acltype,uint16_t userperm,uint16_t groupperm,uint16_t otherperm,uint16_t mask,uint16_t namedusers,uint16_t namedgroups,const uint8_t *aclblob) {
255 	uint16_t i,acls;
256 	acl_node *acn;
257 
258 	if (acltype==POSIX_ACL_ACCESS && ((namedusers | namedgroups) == 0) && userperm<=7 && groupperm<=7 && otherperm<=7 && mask==0xFFFF) {
259 		posix_acl_remove(inode,acltype);
260 		fs_del_aclflag(inode,acltype);
261 		return;
262 	}
263 
264 	acn = GLUE_FN_NAME_PREFIX(_find)(inode,acltype);
265 	if (acn==NULL) {
266 		acn = posix_acl_create(inode,acltype);
267 		fs_set_aclflag(inode,acltype);
268 	}
269 
270 	acls = namedusers + namedgroups;
271 	acn->userperm = userperm;
272 	acn->groupperm = groupperm;
273 	acn->otherperm = otherperm;
274 	acn->mask = mask;
275 	if (acn->namedusers+acn->namedgroups!=acls) {
276 		if (acn->acltab!=NULL) {
277 			free(acn->acltab);
278 		}
279 		if (acls>0) {
280 			acn->acltab = malloc(sizeof(acl_entry)*acls);
281 			passert(acn->acltab);
282 		} else {
283 			acn->acltab = NULL;
284 		}
285 	}
286 	acn->namedusers = namedusers;
287 	acn->namedgroups = namedgroups;
288 //	syslog(LOG_NOTICE,"acls: %u ; acltab: %p ; aclblob: %p",acls,acn->acltab,aclblob);
289 	for (i=0 ; i<acls ; i++) {
290 		acn->acltab[i].id = get32bit(&aclblob);
291 		acn->acltab[i].perm = get16bit(&aclblob);
292 	}
293 }
294 
posix_acl_get_blobsize(uint32_t inode,uint8_t acltype,void ** aclnode)295 int32_t posix_acl_get_blobsize(uint32_t inode,uint8_t acltype,void **aclnode) {
296 	acl_node *acn;
297 	acn = GLUE_FN_NAME_PREFIX(_find)(inode,acltype);
298 
299 	*aclnode = (void*)acn;
300 	if (acn==NULL) {
301 		return -1;
302 	} else {
303 		return (acn->namedusers+acn->namedgroups)*6;
304 	}
305 }
306 
posix_acl_get_data(void * aclnode,uint16_t * userperm,uint16_t * groupperm,uint16_t * otherperm,uint16_t * mask,uint16_t * namedusers,uint16_t * namedgroups,uint8_t * aclblob)307 void posix_acl_get_data(void *aclnode,uint16_t *userperm,uint16_t *groupperm,uint16_t *otherperm,uint16_t *mask,uint16_t *namedusers,uint16_t *namedgroups,uint8_t *aclblob) {
308 	acl_node *acn;
309 	uint16_t i,acls;
310 
311 	acn = (acl_node*)aclnode;
312 
313 	*userperm = acn->userperm;
314 	*groupperm = acn->groupperm;
315 	*otherperm = acn->otherperm;
316 	*mask = acn->mask;
317 	*namedusers = acn->namedusers;
318 	*namedgroups = acn->namedgroups;
319 	acls = acn->namedusers+acn->namedgroups;
320 	for (i=0 ; i<acls ; i++) {
321 		put32bit(&aclblob,acn->acltab[i].id);
322 		put16bit(&aclblob,acn->acltab[i].perm);
323 	}
324 }
325 
posix_acl_copy(uint32_t srcinode,uint32_t dstinode,uint8_t acltype)326 uint8_t posix_acl_copy(uint32_t srcinode,uint32_t dstinode,uint8_t acltype) {
327 	uint32_t acls;
328 	acl_node *sacn,*dacn;
329 
330 	sacn = GLUE_FN_NAME_PREFIX(_find)(srcinode,acltype);
331 	dacn = GLUE_FN_NAME_PREFIX(_find)(dstinode,acltype);
332 	if (dacn==NULL) {
333 		dacn = malloc(sizeof(acl_node));
334 		passert(dacn);
335 		dacn->inode = dstinode;
336 		dacn->acltype = acltype;
337 		GLUE_FN_NAME_PREFIX(_add)(dacn);
338 	} else {
339 		if (dacn->acltab!=NULL) {
340 			free(dacn->acltab);
341 		}
342 	}
343 	dacn->userperm = sacn->userperm;
344 	dacn->groupperm = sacn->groupperm;
345 	dacn->otherperm = sacn->otherperm;
346 	dacn->mask = sacn->mask;
347 	dacn->namedusers = sacn->namedusers;
348 	dacn->namedgroups = sacn->namedgroups;
349 	acls = sacn->namedusers + sacn->namedgroups;
350 	if (acls>0) {
351 		dacn->acltab = malloc(sizeof(acl_entry)*acls);
352 		passert(dacn->acltab);
353 		memcpy(dacn->acltab,sacn->acltab,sizeof(acl_entry)*acls);
354 	} else {
355 		dacn->acltab = NULL;
356 	}
357 	return 1;
358 }
359 
posix_acl_cleanup(void)360 void posix_acl_cleanup(void) {
361 	uint32_t i,j;
362 	acl_node *acn,*nacn;
363 
364 	for (i=0 ; i<HASHTAB_HISIZE ; i++) {
365 		if (GLUE_HASH_TAB_PREFIX(hashtab)[i]!=NULL) {
366 			for (j=0 ; j<HASHTAB_LOSIZE ; j++) {
367 				for (acn=GLUE_HASH_TAB_PREFIX(hashtab)[i][j] ; acn!=NULL ; acn=nacn) {
368 					nacn = acn->next;
369 					if (acn->acltab) {
370 						free(acn->acltab);
371 					}
372 					free(acn);
373 				}
374 				GLUE_HASH_TAB_PREFIX(hashtab)[i][j] = NULL;
375 			}
376 		}
377 	}
378 	GLUE_FN_NAME_PREFIX(_hash_cleanup)();
379 }
380 
posix_acl_store(bio * fd)381 uint8_t posix_acl_store(bio *fd) {
382 	uint8_t hdrbuff[4+1+2*6];
383 	uint8_t aclbuff[6*100];
384 	uint8_t *ptr;
385 	uint32_t i,j,accnt,acbcnt;
386 	acl_node *acn;
387 
388 	if (fd==NULL) {
389 		return 0x11;
390 	}
391 
392 	for (i=0 ; i<HASHTAB_HISIZE ; i++) {
393 		if (GLUE_HASH_TAB_PREFIX(hashtab)[i]!=NULL) {
394 			for (j=0 ; j<HASHTAB_LOSIZE ; j++) {
395 				for (acn=GLUE_HASH_TAB_PREFIX(hashtab)[i][j] ; acn!=NULL ; acn=acn->next) {
396 					ptr = hdrbuff;
397 					put32bit(&ptr,acn->inode);
398 					put8bit(&ptr,acn->acltype);
399 					put16bit(&ptr,acn->userperm);
400 					put16bit(&ptr,acn->groupperm);
401 					put16bit(&ptr,acn->otherperm);
402 					put16bit(&ptr,acn->mask);
403 					put16bit(&ptr,acn->namedusers);
404 					put16bit(&ptr,acn->namedgroups);
405 					if (bio_write(fd,hdrbuff,4+1+2*6)!=(4+1+2*6)) {
406 						syslog(LOG_NOTICE,"write error");
407 						return 0xFF;
408 					}
409 					accnt = 0;
410 					acbcnt = 0;
411 					ptr = aclbuff;
412 					while (accnt<acn->namedusers+acn->namedgroups) {
413 						if (acbcnt==100) {
414 							if (bio_write(fd,aclbuff,6*100)!=(6*100)) {
415 								syslog(LOG_NOTICE,"write error");
416 								return 0xFF;
417 							}
418 							acbcnt = 0;
419 							ptr = aclbuff;
420 						}
421 						put32bit(&ptr,acn->acltab[accnt].id);
422 						put16bit(&ptr,acn->acltab[accnt].perm);
423 						accnt++;
424 						acbcnt++;
425 					}
426 					if (acbcnt>0) {
427 						if (bio_write(fd,aclbuff,6*acbcnt)!=(6*acbcnt)) {
428 							syslog(LOG_NOTICE,"write error");
429 							return 0xFF;
430 						}
431 					}
432 				}
433 			}
434 		}
435 	}
436 	memset(hdrbuff,0,4+1+2*6);
437 	if (bio_write(fd,hdrbuff,4+1+2*6)!=(4+1+2*6)) {
438 		syslog(LOG_NOTICE,"write error");
439 		return 0xFF;
440 	}
441 	return 0;
442 }
443 
posix_acl_load(bio * fd,uint8_t mver,int ignoreflag)444 int posix_acl_load(bio *fd,uint8_t mver,int ignoreflag) {
445 	uint8_t hdrbuff[4+1+2*6];
446 	uint8_t aclbuff[6*100];
447 	const uint8_t *ptr;
448 	uint32_t inode;
449 	uint8_t acltype;
450 	uint16_t userperm;
451 	uint16_t groupperm;
452 	uint16_t otherperm;
453 	uint16_t mask;
454 	uint16_t namedusers;
455 	uint16_t namedgroups;
456 	uint32_t i,acls,acbcnt;
457 	uint8_t nl=1;
458 	acl_node *acn;
459 
460 	while (1) {
461 		if (bio_read(fd,hdrbuff,4+1+2*6)!=(4+1+2*6)) {
462 			int err = errno;
463 			if (nl) {
464 				fputc('\n',stderr);
465 				// nl=0;
466 			}
467 			errno = err;
468 			mfs_errlog(LOG_ERR,"loading posix_acl: read error");
469 			return -1;
470 		}
471 		ptr = hdrbuff;
472 		inode = get32bit(&ptr);
473 		if (inode==0) {
474 			return 1;
475 		}
476 		acltype = get8bit(&ptr);
477 		userperm = get16bit(&ptr);
478 		groupperm = get16bit(&ptr);
479 		otherperm = get16bit(&ptr);
480 		mask = get16bit(&ptr);
481 		namedusers = get16bit(&ptr);
482 		namedgroups = get16bit(&ptr);
483 		acls = namedusers + namedgroups;
484 		if (fs_check_inode(inode)==0) { // silently skip acl's for non-existent inodes
485 			bio_skip(fd,6*acls);
486 			continue;
487 		}
488 		if (acltype!=POSIX_ACL_ACCESS && acltype!=POSIX_ACL_DEFAULT) {
489 			if (nl) {
490 				fputc('\n',stderr);
491 				nl=0;
492 			}
493 			mfs_syslog(LOG_ERR,"loading posix_acl: wrong acl type");
494 			if (ignoreflag) {
495 				bio_skip(fd,6*acls);
496 				continue;
497 			} else {
498 				return -1;
499 			}
500 		}
501 		acn = GLUE_FN_NAME_PREFIX(_find)(inode,acltype);
502 		if (acn!=NULL) {
503 			if (nl) {
504 				fputc('\n',stderr);
505 				nl=0;
506 			}
507 			mfs_syslog(LOG_ERR,"loading posix_acl: repeated acl");
508 			if (ignoreflag) {
509 				bio_skip(fd,6*acls);
510 				continue;
511 			} else {
512 				return -1;
513 			}
514 		}
515 		acn = posix_acl_create(inode,acltype);
516 		fs_set_aclflag(inode,acltype);
517 		if (mver==0x10 && mask==0) {
518 			uint16_t mode;
519 			mode = fs_get_mode(inode);
520 			userperm &= 0xFFF8;
521 			userperm |= (mode>>6)&7;
522 			mask = 7;
523 			otherperm &= 0xFFF8;
524 			otherperm |= mode&7;
525 			mfs_arg_syslog(LOG_WARNING,"emergency set ACL mask for inode %"PRIu32" to 'rwx'",inode);
526 		}
527 		acn->userperm = userperm;
528 		acn->groupperm = groupperm;
529 		acn->otherperm = otherperm;
530 		acn->mask = mask;
531 		acn->namedusers = namedusers;
532 		acn->namedgroups = namedgroups;
533 		if (acls>0) {
534 			acn->acltab = malloc(sizeof(acl_entry)*acls);
535 			passert(acn->acltab);
536 		} else {
537 			acn->acltab = NULL;
538 		}
539 		acbcnt = 0;
540 		for (i=0 ; i<acls ; i++) {
541 			if (acbcnt==0) {
542 				acbcnt = acls-i;
543 				if (acbcnt>100) {
544 					acbcnt=100;
545 				}
546 				if (bio_read(fd,aclbuff,6*acbcnt)!=6*acbcnt) {
547 					int err = errno;
548 					if (nl) {
549 						fputc('\n',stderr);
550 						// nl=0;
551 					}
552 					GLUE_FN_NAME_PREFIX(_delete)(acn);
553 					if (acn->acltab!=NULL) {
554 						free(acn->acltab);
555 					}
556 					free(acn);
557 					errno = err;
558 					mfs_errlog(LOG_ERR,"loading posix_acl: read error");
559 					return -1;
560 				}
561 				ptr = aclbuff;
562 			}
563 			acn->acltab[i].id = get32bit(&ptr);
564 			acn->acltab[i].perm = get16bit(&ptr);
565 			acbcnt--;
566 		}
567 	}
568 }
569 
posix_acl_init(void)570 int posix_acl_init(void) {
571 	GLUE_FN_NAME_PREFIX(_hash_init)();
572 	return 0;
573 }
574 
575 #include "hash_end.h"
576 
577 #undef LOHASH_BITS
578 #undef ENTRY_TYPE
579 #undef GLUE_FN_NAME_PREFIX
580 #undef HASH_ARGS_TYPE_LIST
581 #undef HASH_ARGS_LIST
582 #undef GLUE_HASH_TAB_PREFIX
583