1// beos_stat_cache.c
2
3#include <errno.h>
4#include <stdio.h>
5#include <stdlib.h>
6
7#include <OS.h>
8
9#include "beos_stat_cache.h"
10#include "pathsys.h"
11#include "StatCacheServer.h"
12
13#define SET_ERRNO_AND_RETURN(error) {	\
14	if ((error) == B_OK)				\
15		return 0;						\
16	errno = (error);					\
17	return -1;							\
18}
19
20// get_server_port
21static
22port_id
23get_server_port()
24{
25	static port_id id = -1;
26	static bool initialized = false;
27	if (!initialized) {
28		id = find_port(STAT_CACHE_SERVER_PORT_NAME);
29		initialized = true;
30	}
31	return id;
32}
33
34// get_reply_port
35static
36port_id
37get_reply_port()
38{
39	static port_id id = -1;
40	if (id < 0)
41		id = create_port(1, "stat cache reply port");
42	return id;
43}
44
45// send_request
46static
47status_t
48send_request(int32 command, const char *path)
49{
50	port_id requestPort = get_server_port();
51	port_id replyPort = get_reply_port();
52	stat_cache_request request;
53	int requestSize;
54
55	// get request port
56	if (requestPort < 0)
57		return requestPort;
58	// get reply port
59	if (replyPort < 0)
60		return replyPort;
61	// normalize the path
62	if (!path || !normalize_path(path, request.path, sizeof(request.path)))
63		return B_BAD_VALUE;
64	requestSize = (request.path + strlen(request.path) + 1) - (char*)&request;
65	// send request
66	request.replyPort = replyPort;
67	request.command = command;
68	return write_port(requestPort, 0, &request, requestSize);
69}
70
71// receive_reply
72static
73status_t
74receive_reply(void **_reply, int32 *_replySize, void *buffer, int32 replySize)
75{
76	port_id replyPort = get_reply_port();
77	ssize_t bytesRead;
78	void *reply;
79	int32 code;
80
81	// get reply port
82	if (replyPort < 0)
83		return replyPort;
84
85	// get the reply size
86	if (!buffer) {
87		replySize = port_buffer_size(replyPort);
88		if (replySize < 0)
89			return replySize;
90	}
91
92	// allocate reply
93	if (buffer) {
94		reply = buffer;
95	} else {
96		reply = malloc(replySize);
97		if (!reply)
98			return B_NO_MEMORY;
99	}
100
101	// read the reply
102	bytesRead = read_port(replyPort, &code, reply, replySize);
103	if (bytesRead < 0) {
104		if (!buffer)
105			free(reply);
106		return bytesRead;
107	}
108	if (bytesRead != replySize) {
109		if (!buffer)
110			free(reply);
111		return B_ERROR;
112	}
113
114	if (_reply)
115		*_reply = reply;
116	if (_replySize)
117		*_replySize = replySize;
118	return B_OK;
119}
120
121// beos_stat_cache_stat
122int
123beos_stat_cache_stat(const char *filename, struct stat *st)
124{
125	stat_cache_stat_reply reply;
126	status_t error;
127
128	// fall back to standard, if there is no server
129	if (get_server_port() < 0)
130		return stat(filename, st);
131
132	// send the request
133	error = send_request(STAT_CACHE_COMMAND_STAT, filename);
134	if (error != B_OK)
135		SET_ERRNO_AND_RETURN(error);
136
137	// get the reply
138	error = receive_reply(NULL, NULL, &reply, sizeof(reply));
139	if (error != B_OK)
140		error = reply.error;
141	if (error != B_OK)
142		SET_ERRNO_AND_RETURN(error);
143
144	*st = reply.st;
145	return 0;
146}
147
148// beos_stat_cache_opendir
149DIR *
150beos_stat_cache_opendir(const char *dirName)
151{
152	stat_cache_readdir_reply *reply;
153	int32 replySize;
154	status_t error;
155
156	// fall back to standard, if there is no server
157	if (get_server_port() < 0)
158		return opendir(dirName);
159
160	// send the request
161	error = send_request(STAT_CACHE_COMMAND_READDIR, dirName);
162	if (error != B_OK) {
163		errno = error;
164		return NULL;
165	}
166
167	// get the reply
168	error = receive_reply((void**)&reply, &replySize, NULL, 0);
169	if (error != B_OK)
170		error = reply->error;
171	if (error != B_OK) {
172		free(reply);
173		errno = error;
174		return NULL;
175	}
176
177	reply->clientData = reply->buffer;
178
179	// a bit ugly, but anyway...
180	return (DIR*)reply;
181}
182
183// beos_stat_cache_readdir
184struct dirent *
185beos_stat_cache_readdir(DIR *dir)
186{
187	stat_cache_readdir_reply *reply;
188	struct dirent *entry;
189
190	// fall back to standard, if there is no server
191	if (get_server_port() < 0)
192		return readdir(dir);
193
194	reply = (stat_cache_readdir_reply*)dir;
195	if (reply->entryCount == 0)
196		return NULL;
197
198	entry = (struct dirent*)reply->clientData;
199
200	// get the next entry
201	if (--reply->entryCount > 0)
202		reply->clientData = (uint8*)entry + entry->d_reclen;
203
204	return entry;
205}
206
207// beos_stat_cache_closedir
208int
209beos_stat_cache_closedir(DIR *dir)
210{
211	stat_cache_readdir_reply *reply;
212
213	// fall back to standard, if there is no server
214	if (get_server_port() < 0)
215		return closedir(dir);
216
217	reply = (stat_cache_readdir_reply*)dir;
218	free(reply);
219	return 0;
220}
221
222