1 /*
2  * libhfsuser - Userspace support library for NetBSD's libhfs
3  * Copyright 2013-2017 0x09.net.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include "cache.h"
26 
27 #include <stdlib.h>
28 #include <pthread.h>
29 
30 struct ringnode {
31 	struct ringnode* next,* prev;
32 	char* path;
33 	size_t len;
34 	hfs_catalog_keyed_record_t record;
35 	hfs_catalog_key_t key;
36 };
37 
38 struct hfs_record_cache {
39 	pthread_rwlock_t lock;
40 	struct ringnode* head;
41 	struct ringnode backing[];
42 };
43 
hfs_record_cache_create(size_t length)44 struct hfs_record_cache* hfs_record_cache_create(size_t length) {
45 	if(!length)
46 		return NULL;
47 	struct hfs_record_cache* buf = malloc(sizeof(*buf) + sizeof(*buf->backing)*length);
48 	if(!buf || pthread_rwlock_init(&buf->lock,NULL)) {
49 		free(buf);
50 		return NULL;
51 	}
52 
53 	struct ringnode* tail = buf->head = buf->backing;
54 	for(size_t i = 0; i <= length; i++) {
55 		tail->next = buf->backing + i%length;
56 		tail->next->prev = tail;
57 		tail = tail->next;
58 		tail->path = NULL;
59 	}
60 	return buf;
61 }
62 
hfs_record_cache_destroy(struct hfs_record_cache * buf)63 void hfs_record_cache_destroy(struct hfs_record_cache* buf) {
64 	if(!buf)
65 		return;
66     pthread_rwlock_wrlock(&buf->lock);
67 	struct ringnode* tail = buf->head;
68 	do {
69 		free(tail->path);
70 		tail = tail->next;
71 	} while(tail != buf->head);
72 	pthread_rwlock_unlock(&buf->lock);
73 	pthread_rwlock_destroy(&buf->lock);
74 	free(buf);
75 }
76 
hfs_record_cache_lookup(struct hfs_record_cache * buf,const char * path,size_t len,hfs_catalog_keyed_record_t * record,hfs_catalog_key_t * key)77 bool hfs_record_cache_lookup(struct hfs_record_cache* buf, const char* path, size_t len, hfs_catalog_keyed_record_t* record, hfs_catalog_key_t* key) {
78 	bool ret = false;
79 	if(!buf || pthread_rwlock_rdlock(&buf->lock))
80 		return ret;
81 	struct ringnode* it = buf->head;
82 	do {
83 		if(!it->path) break;
84 		if(len == it->len && !memcmp(it->path,path,len)) {
85 			*record = it->record;
86 			*key = it->key;
87 			ret = true;
88 			break;
89 		}
90 		it = it->next;
91 	} while(it != buf->head);
92 	pthread_rwlock_unlock(&buf->lock);
93 	return ret;
94 }
95 
hfs_record_cache_lookup_parents(struct hfs_record_cache * buf,char * path,size_t len,hfs_catalog_keyed_record_t * record,hfs_catalog_key_t * key)96 size_t hfs_record_cache_lookup_parents(struct hfs_record_cache* buf, char* path, size_t len, hfs_catalog_keyed_record_t* record, hfs_catalog_key_t* key) {
97 	char* c;
98 	while((c = strrchr(path, '/'))) {
99 		*c = '\0';
100 		len = c - path;
101 		if(*path && hfs_record_cache_lookup(buf, path, len, record, key))
102 			break;
103 	}
104 	return len;
105 }
106 
hfs_record_cache_add(struct hfs_record_cache * buf,const char * path,size_t len,hfs_catalog_keyed_record_t * record,hfs_catalog_key_t * key)107 void hfs_record_cache_add(struct hfs_record_cache* buf, const char* path, size_t len, hfs_catalog_keyed_record_t* record, hfs_catalog_key_t* key) {
108 	if(!buf || pthread_rwlock_wrlock(&buf->lock))
109 		return;
110 	struct ringnode* tail = buf->head->prev;
111 	char* newpath = realloc(tail->path,len);
112 	if(!newpath) {
113 		do {
114 			free(tail->path);
115 			tail->path = NULL;
116 			tail = tail->next;
117 		} while(tail != buf->head->prev);
118 		goto end;
119 	}
120 	memcpy(newpath, path,len);
121 	tail->path = newpath;
122 	tail->len = len;
123 	tail->key = *key;
124 	tail->record = *record;
125 	buf->head = tail;
126 end:
127 	pthread_rwlock_unlock(&buf->lock);
128 }
129