3 * UNFS3 cluster support
4 * (C) 2004, Pascal Schmidt
5 * see file LICENSE for license details
12 #include <sys/types.h>
14 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
28 #include "../daemon.h"
29 #include "../backend.h"
32 /* array of dirents prefixed with master file name */
33 static char **cluster_dirents = NULL;
35 /* number of dirents in above array */
36 static int cluster_count = -1;
39 * check whether given pathname is in clustering path
41 int want_cluster(const char *path)
43 char buf[NFS_MAXPATHLEN];
46 /* if path is too long, play it safe */
47 if (strlen(opt_cluster_path) + 1 > NFS_MAXPATHLEN)
50 strcpy(buf, opt_cluster_path);
53 /* iterate over colon-seperated list */
55 next = strchr(last, ':');
59 if (strstr(path, last) == path)
64 if (strlen(last) == 0)
75 * get name of remote machine
77 static char *get_host(struct in_addr remote)
79 static char buf[NFS_MAXPATHLEN];
80 struct hostent *entry;
83 entry = gethostbyaddr((char *) &remote, sizeof(struct in_addr), AF_INET);
86 strcpy(buf, entry->h_name);
88 /* have the name string end at the first dot */
89 dot = strchr(buf, '.');
100 * check whether name is already host tagged name
102 int is_host(const char *name)
104 return (int) (strstr(name, "$$HOST=") && name[strlen(name) - 1] == '$' &&
105 name[strlen(name) - 2] == '$');
109 * check whether a hostname matches a dirent
111 char *match_host(const char *hname, const char *entry)
113 char buf[NFS_MAXPATHLEN];
116 /* check for presence of hostname tag */
120 part = strstr(entry, "$$HOST=");
122 /* copy hostname part of host tag */
123 memcpy(buf, part + 7, strlen(part) - 8);
124 buf[strlen(part) - 9] = 0;
127 if (strcmp(buf, hname) == 0)
130 /* wildcard pattern? */
131 if (buf[strlen(buf) - 1] != '*')
134 /* if wildcard, check for matching prefix */
135 buf[strlen(buf) - 1] = 0;
136 if (strstr(hname, buf) == hname)
143 * better dirname providing internal buffer
145 char *cluster_dirname(const char *path)
147 static char buf[NFS_MAXPATHLEN];
154 * better basename providing internal buffer
156 char *cluster_basename(const char *path)
158 static char buf[NFS_MAXPATHLEN];
161 return basename(buf);
167 void cluster_freedir(void)
169 /* only if it was really allocated before */
170 if (cluster_dirents) {
171 while (cluster_count--)
172 free(cluster_dirents[cluster_count]);
173 free(cluster_dirents);
174 cluster_dirents = NULL;
179 * compare function for qsort'ing the scandir list
181 int compar(const void *x, const void *y)
183 return strcmp(*(const char **) x, *(const char **) y);
187 * reset euid/egid to specific values
189 static void reset_ids(uid_t euid, gid_t egid)
191 if (backend_setegid(egid) || backend_seteuid(euid)) {
192 logmsg(LOG_EMERG, "euid/egid switching failed, aborting");
198 * scan directory for filenames beginning with master name as prefix
200 void cluster_scandir(const char *path)
202 char prefix[NFS_MAXPATHLEN];
204 struct dirent *entry;
209 strcpy(prefix, cluster_basename(path));
212 * need to read directory as root, temporarily switch back
214 euid = backend_geteuid();
215 egid = backend_getegid();
219 scan = backend_opendir(cluster_dirname(path));
222 reset_ids(euid, egid);
227 while ((entry = backend_readdir(scan))) {
228 if (strstr(entry->d_name, prefix) != entry->d_name &&
229 strcmp(entry->d_name, "$$CREATE=IP$$") != 0 &&
230 strcmp(entry->d_name, "$$CREATE=CLIENT$$") != 0 &&
231 strcmp(entry->d_name, "$$ALWAYS=IP$$") != 0 &&
232 strcmp(entry->d_name, "$$ALWAYS=CLIENT$$") != 0)
235 name = malloc(strlen(entry->d_name) + 1);
236 new = realloc(cluster_dirents, (cluster_count + 1) * sizeof(char *));
242 backend_closedir(scan);
243 reset_ids(euid, egid);
247 strcpy(name, entry->d_name);
248 cluster_dirents = new;
249 cluster_dirents[cluster_count] = name;
253 backend_closedir(scan);
254 reset_ids(euid, egid);
256 /* list needs to be sorted for cluster_lookup_lowlevel to work */
257 qsort(cluster_dirents, cluster_count, sizeof(char *), compar);
261 * check whether master name + suffix matches with a string
263 int match_suffix(const char *master, const char *suffix, const char *entry)
265 char obj[NFS_MAXPATHLEN];
267 sprintf(obj, "%s%s", master, suffix);
269 if (strcmp(entry, obj) == 0)
276 * create string version of a netmask
277 * buf: where to put string
278 * remote: full IP address of remote machine
279 * n: number of dots to keep
281 void cluster_netmask(char *buf, const char *remote, int n)
285 sprintf(buf, "$$IP=%s", remote);
287 /* skip to desired dot position */
288 for (i = 0; i < n; i++)
289 buf = strchr(buf, '.') + 1;
293 /* append trailer of netmask string */
296 strcat(buf, "0_24$$");
299 strcat(buf, "0.0_16$$");
302 strcat(buf, "0.0.0_8$$");
308 * look up cluster name, defaulting to master name if no slave name found
310 int cluster_lookup_lowlevel(char *path, struct svc_req *rqstp)
312 struct in_addr raddr;
313 char *remote, *hname, *master, *entry, *match;
314 char buf[NFS_MAXPATHLEN];
315 int i, res = CLU_MASTER;
318 cluster_scandir(path);
320 if (cluster_count == -1)
322 else if (cluster_count == 0)
325 raddr = get_remote(rqstp); /* remote IP address */
326 master = cluster_basename(path); /* master file name */
327 remote = inet_ntoa(raddr); /* remote IP address string */
328 hname = get_host(raddr); /* remote hostname */
331 * traversal in reverse alphanumerical order, so that
332 * IP is encountered before HOST, HOST before CLIENT,
333 * CLIENT before ALWAYS, and also subnets are encountered
338 entry = cluster_dirents[i];
340 /* match specific IP address */
341 sprintf(buf, "$$IP=%s$$", remote);
342 if ((res = match_suffix(master, buf, entry)))
345 /* always match IP file */
346 if ((res = match_suffix(master, "$$ALWAYS=IP$$", entry)))
348 if (strcmp("$$ALWAYS=IP$$", entry) == 0) {
353 /* match all clients */
354 strcpy(buf, "$$CLIENT$$");
355 if ((res = match_suffix(master, buf, entry)))
358 /* always match CLIENT file */
359 if ((res = match_suffix(master, "$$ALWAYS=CLIENT$$", entry)))
361 if (strcmp("$$ALWAYS=CLIENT$$", entry) == 0) {
366 /* match 24 bit network address */
367 cluster_netmask(buf, remote, 3);
368 if ((res = match_suffix(master, buf, entry)))
371 /* match 16 bit network address */
372 cluster_netmask(buf, remote, 2);
373 if ((res = match_suffix(master, buf, entry)))
376 /* match 8 bit network address */
377 cluster_netmask(buf, remote, 1);
378 if ((res = match_suffix(master, buf, entry)))
381 /* match hostname pattern */
382 if (!is_host(master)) {
383 match = match_host(hname, entry);
392 /* append suffix if possible */
393 if (res == CLU_SLAVE) {
394 if (strlen(path) + strlen(buf) + 1 < NFS_MAXPATHLEN)
399 /* res will be 0 after above loop */
404 * dirent array not freed here since cluster_create may need
405 * to look at it afterwards
412 * substitute slave filename if possible
414 void cluster_lookup(char *path, struct svc_req *rqstp, nfsstat3 * nstat)
424 if (*nstat != NFS3_OK)
427 if (!want_cluster(path))
431 if (strstr(path, "$$$$") == path + res - 4) {
432 *(path + res - 4) = 0;
436 res = cluster_lookup_lowlevel(path, rqstp);
437 if (res == CLU_TOOLONG)
438 *nstat = NFS3ERR_NAMETOOLONG;
439 else if (res == CLU_IO)
444 * substitute slave filename if possible, for create operations
446 void cluster_create(char *path, struct svc_req *rqstp, nfsstat3 * nstat)
449 char buf[NFS_MAXPATHLEN];
450 char *master, *entry;
455 if (*nstat != NFS3_OK)
458 if (!want_cluster(path))
461 res = cluster_lookup_lowlevel(path, rqstp);
463 if (res == CLU_TOOLONG) {
464 *nstat = NFS3ERR_NAMETOOLONG;
466 } else if (res == CLU_IO) {
469 } else if (res == CLU_SLAVE)
472 master = cluster_basename(path);
474 /* look for create tag */
477 entry = cluster_dirents[i];
479 /* always create IP file */
480 sprintf(buf, "$$IP=%s$$", inet_ntoa(get_remote(rqstp)));
481 if ((res = match_suffix(master, "$$CREATE=IP$$", entry)) ||
482 (res = match_suffix(master, "$$ALWAYS=IP$$", entry)))
484 if ((strcmp("$$CREATE=IP$$", entry) == 0) ||
485 (strcmp("$$ALWAYS=IP$$", entry) == 0)) {
490 /* always create CLIENT file */
491 sprintf(buf, "$$CLIENT$$");
492 if ((res = match_suffix(master, "$$CREATE=CLIENT$$", entry)) ||
493 (res = match_suffix(master, "$$ALWAYS=CLIENT$$", entry)))
495 if ((strcmp("$$CREATE=CLIENT$$", entry) == 0) ||
496 (strcmp("$$ALWAYS=CLIENT$$", entry) == 0)) {
502 if (res != CLU_SLAVE)
505 /* append suffix if possible */
506 if (strlen(path) + strlen(buf) + 1 < NFS_MAXPATHLEN)
509 *nstat = NFS3ERR_NAMETOOLONG;