]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/dbd/cnid_dbd.c
32be8f475408fc644d21d010dc9e76091678d9e4
[netatalk.git] / libatalk / cnid / dbd / cnid_dbd.c
1 /*
2  * Copyright (C) Joerg Lenneis 2003
3  * Copyright (C) Frank Lahm 2010
4  * All Rights Reserved.  See COPYING.
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif /* HAVE_CONFIG_H */
10
11 #ifdef CNID_BACKEND_DBD
12
13 #include <stdlib.h>
14 #include <sys/stat.h>
15 #include <sys/uio.h>
16 #include <sys/time.h>
17 #include <sys/un.h>
18 #include <sys/socket.h>
19 #include <sys/param.h>
20 #include <errno.h>
21 #include <netinet/in.h>
22 #include <net/if.h>
23 #include <netinet/tcp.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <errno.h>
27 #include <netdb.h>
28 #include <time.h>
29 #include <arpa/inet.h>
30
31 #include <atalk/logger.h>
32 #include <atalk/adouble.h>
33 #include <atalk/cnid.h>
34 #include <atalk/cnid_dbd_private.h>
35 #include <atalk/util.h>
36
37 #include "cnid_dbd.h"
38
39 #ifndef SOL_TCP
40 #define SOL_TCP IPPROTO_TCP
41 #endif /* ! SOL_TCP */
42
43 /* Wait MAX_DELAY seconds before a request to the CNID server times out */
44 #define MAX_DELAY 20
45 #define ONE_DELAY 5
46
47 static void RQST_RESET(struct cnid_dbd_rqst  *r)
48 {
49     memset(r, 0, sizeof(struct cnid_dbd_rqst ));
50 }
51
52 static void delay(int sec)
53 {
54     struct timeval tv;
55
56     tv.tv_usec = 0;
57     tv.tv_sec  = sec;
58     select(0, NULL, NULL, NULL, &tv);
59 }
60
61 static int tsock_getfd(const char *host, const char *port)
62 {
63     int sock = -1;
64     int attr;
65     int err;
66     struct addrinfo hints, *servinfo, *p;
67     int optval;
68     socklen_t optlen = sizeof(optval);
69
70     /* Prepare hint for getaddrinfo */
71     memset(&hints, 0, sizeof hints);
72     hints.ai_family = AF_UNSPEC;
73     hints.ai_socktype = SOCK_STREAM;
74 #ifdef AI_NUMERICSERV
75     hints.ai_flags = AI_NUMERICSERV;
76 #endif
77
78     if ((err = getaddrinfo(host, port, &hints, &servinfo)) != 0) {
79         LOG(log_error, logtype_default, "tsock_getfd: getaddrinfo: CNID server %s:%s : %s\n",
80             host, port, gai_strerror(err));
81         return -1;
82     }
83
84     /* loop through all the results and bind to the first we can */
85     for (p = servinfo; p != NULL; p = p->ai_next) {
86         if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
87             LOG(log_info, logtype_default, "tsock_getfd: socket CNID server %s:: %s",
88                 host, strerror(errno));
89             continue;
90         }
91
92         attr = 1;
93         if (setsockopt(sock, SOL_TCP, TCP_NODELAY, &attr, sizeof(attr)) == -1) {
94             LOG(log_error, logtype_cnid, "getfd: set TCP_NODELAY CNID server %s: %s",
95                 host, strerror(errno));
96             close(sock);
97             sock = -1;
98             return -1;
99         }
100
101         if (setnonblock(sock, 1) != 0) {
102             LOG(log_error, logtype_cnid, "getfd: setnonblock: %s", strerror(errno));
103             close(sock);
104             sock = -1;
105             return -1;
106         }
107
108         if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
109             if (errno == EINPROGRESS) {
110                 struct timeval tv;
111                 tv.tv_usec = 0;
112                 tv.tv_sec  = 5; /* give it five seconds ... */
113                 fd_set wfds;
114                 FD_ZERO(&wfds);
115                 FD_SET(sock, &wfds);
116
117                 if ((err = select(sock + 1, NULL, &wfds, NULL, &tv)) == 0) {
118                     /* timeout */
119                     LOG(log_error, logtype_cnid, "getfd: select timed out for CNID server %s",
120                         host);
121                     close(sock);
122                     sock = -1;
123                     continue;
124                 }
125                 if (err == -1) {
126                     /* select failed */
127                     LOG(log_error, logtype_cnid, "getfd: select failed for CNID server %s",
128                         host);
129                     close(sock);
130                     sock = -1;
131                     continue;
132                 }
133
134                 if ( ! FD_ISSET(sock, &wfds)) {
135                     /* give up */
136                     LOG(log_error, logtype_cnid, "getfd: socket not ready connecting to %s",
137                         host);
138                     close(sock);
139                     sock = -1;
140                     continue;
141                 }
142
143                 if ((err = getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen)) != 0 || optval != 0) {
144                     if (err != 0) {
145                         /* somethings very wrong */
146                         LOG(log_error, logtype_cnid, "getfd: getsockopt error with CNID server %s: %s",
147                             host, strerror(errno));
148                     } else {
149                         errno = optval;
150                         LOG(log_error, logtype_cnid, "getfd: getsockopt says: %s",
151                             strerror(errno));
152                     }
153                     close(sock);
154                     sock = -1;
155                     continue;
156                 }
157             } else {
158                 LOG(log_error, logtype_cnid, "getfd: connect CNID server %s: %s",
159                     host, strerror(errno));
160                 close(sock);
161                 sock = -1;
162                 continue;
163             }
164         }
165
166         /* We've got a socket */
167         break;
168     }
169
170     freeaddrinfo(servinfo);
171
172     if (p == NULL) {
173         errno = optval;
174         LOG(log_error, logtype_cnid, "tsock_getfd: no suitable network config from CNID server (%s:%s): %s",
175             host, port, strerror(errno));
176         return -1;
177     }
178
179     return(sock);
180 }
181
182 /*!
183  * Write "towrite" bytes using writev on non-blocking fd
184  *
185  * Every short write is considered an error, transmit can handle that.
186  *
187  * @param fd      (r) socket fd which must be non-blocking
188  * @param iov     (r) iovec for writev
189  * @param towrite (r) number of bytes in all iovec elements
190  * @param vecs    (r) number of iovecs in array
191  *
192  * @returns "towrite" bytes written or -1 on error
193  */
194 static int write_vec(int fd, struct iovec *iov, ssize_t towrite, int vecs)
195 {
196     ssize_t len;
197     int slept = 0;
198     int sleepsecs;
199
200     while (1) {
201         if (((len = writev(fd, iov, vecs)) == -1 && errno == EINTR))
202             continue;
203
204         if ((! slept) && len == -1 && errno == EAGAIN) {
205             sleepsecs = 2;
206             while ((sleepsecs = sleep(sleepsecs)));
207             slept = 1;
208             continue;
209         }
210
211         if (len == towrite) /* wrote everything out */
212             break;
213
214         if (len == -1)
215             LOG(log_error, logtype_cnid, "write_vec: %s", strerror(errno));
216         else
217             LOG(log_error, logtype_cnid, "write_vec: short write: %d", len);
218         return len;
219     }
220
221     LOG(log_maxdebug, logtype_cnid, "write_vec: wrote %d bytes", len);
222
223     return len;
224 }
225
226 /* --------------------- */
227 static int init_tsock(CNID_private *db)
228 {
229     int fd;
230     int len;
231     struct iovec iov[2];
232
233     LOG(log_debug, logtype_cnid, "init_tsock: BEGIN. Opening volume '%s', CNID Server: %s/%s",
234         db->db_dir, db->cnidserver, db->cnidport);
235
236     if ((fd = tsock_getfd(db->cnidserver, db->cnidport)) < 0)
237         return -1;
238
239     len = strlen(db->db_dir);
240
241     iov[0].iov_base = &len;
242     iov[0].iov_len  = sizeof(int);
243
244     iov[1].iov_base = db->db_dir;
245     iov[1].iov_len  = len;
246
247     if (write_vec(fd, iov, len + sizeof(int), 2) != len + sizeof(int)) {
248         LOG(log_error, logtype_cnid, "init_tsock: Error/short write: %s", strerror(errno));
249         close(fd);
250         return -1;
251     }
252
253     LOG(log_debug, logtype_cnid, "init_tsock: ok");
254
255     return fd;
256 }
257
258 /* --------------------- */
259 static int send_packet(CNID_private *db, struct cnid_dbd_rqst *rqst)
260 {
261     struct iovec iov[2];
262     size_t towrite;
263     int vecs;
264
265     iov[0].iov_base = rqst;
266     iov[0].iov_len  = sizeof(struct cnid_dbd_rqst);
267     towrite = sizeof(struct cnid_dbd_rqst);
268     vecs = 1;
269
270     if (rqst->namelen) {
271         iov[1].iov_base = (char *)rqst->name;
272         iov[1].iov_len  = rqst->namelen;
273         towrite += rqst->namelen;
274         vecs++;
275     }
276
277     if (write_vec(db->fd, iov, towrite, vecs) != towrite) {
278         LOG(log_warning, logtype_cnid, "send_packet: Error writev rqst (db_dir %s): %s",
279             db->db_dir, strerror(errno));
280         return -1;
281     }
282
283     LOG(log_maxdebug, logtype_cnid, "send_packet: {done}");
284     return 0;
285 }
286
287 /* ------------------- */
288 static void dbd_initstamp(struct cnid_dbd_rqst *rqst)
289 {
290     RQST_RESET(rqst);
291     rqst->op = CNID_DBD_OP_GETSTAMP;
292 }
293
294 /* ------------------- */
295 static int dbd_reply_stamp(struct cnid_dbd_rply *rply)
296 {
297     switch (rply->result) {
298     case CNID_DBD_RES_OK:
299         break;
300     case CNID_DBD_RES_NOTFOUND:
301         return -1;
302     case CNID_DBD_RES_ERR_DB:
303     default:
304         errno = CNID_ERR_DB;
305         return -1;
306     }
307     return 0;
308 }
309
310 /* ---------------------
311  * send a request and get reply
312  * assume send is non blocking
313  * if no answer after sometime (at least MAX_DELAY secondes) return an error
314  */
315 static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
316 {
317     ssize_t ret;
318     char *nametmp;
319     size_t len;
320
321     if (send_packet(db, rqst) < 0) {
322         return -1;
323     }
324     len = rply->namelen;
325     nametmp = rply->name;
326
327     ret = readt(db->fd, rply, sizeof(struct cnid_dbd_rply), 0, ONE_DELAY);
328
329     if (ret != sizeof(struct cnid_dbd_rply)) {
330         LOG(log_debug, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
331             db->db_dir, ret == -1 ? strerror(errno) : "closed");
332         rply->name = nametmp;
333         return -1;
334     }
335     rply->name = nametmp;
336     if (rply->namelen && rply->namelen > len) {
337         LOG(log_error, logtype_cnid,
338             "dbd_rpc: Error reading name (db_dir %s): %s name too long: %d. only wanted %d, garbage?",
339             db->db_dir, rply->name, rply->namelen, len);
340         return -1;
341     }
342     if (rply->namelen && (ret = readt(db->fd, rply->name, rply->namelen, 0, ONE_DELAY)) != (ssize_t)rply->namelen) {
343         LOG(log_error, logtype_cnid, "dbd_rpc: Error reading name from fd (db_dir %s): %s",
344             db->db_dir, ret == -1?strerror(errno):"closed");
345         return -1;
346     }
347
348     LOG(log_maxdebug, logtype_cnid, "dbd_rpc: {done}");
349
350     return 0;
351 }
352
353 /* -------------------- */
354 static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
355 {
356     time_t orig, t;
357     int clean = 1; /* no errors so far - to prevent sleep on first try */
358
359     if (db->changed) {
360         /* volume and db don't have the same timestamp
361          */
362         return -1;
363     }
364     while (1) {
365         if (db->fd == -1) {
366             struct cnid_dbd_rqst rqst_stamp;
367             struct cnid_dbd_rply rply_stamp;
368             char  stamp[ADEDLEN_PRIVSYN];
369
370             LOG(log_maxdebug, logtype_cnid, "transmit: connecting to cnid_dbd ...");
371             if ((db->fd = init_tsock(db)) < 0) {
372                 goto transmit_fail;
373             }
374             dbd_initstamp(&rqst_stamp);
375             memset(stamp, 0, ADEDLEN_PRIVSYN);
376             rply_stamp.name = stamp;
377             rply_stamp.namelen = ADEDLEN_PRIVSYN;
378
379             if (dbd_rpc(db, &rqst_stamp, &rply_stamp) < 0)
380                 goto transmit_fail;
381             if (dbd_reply_stamp(&rply_stamp ) < 0)
382                 goto transmit_fail;
383
384             if (db->notfirst) {
385                 LOG(log_debug7, logtype_cnid, "transmit: reconnected to cnid_dbd, comparing database stamps...");
386                 if (memcmp(stamp, db->stamp, ADEDLEN_PRIVSYN)) {
387                     LOG(log_error, logtype_cnid, "transmit: ... not the same db!");
388                     db->changed = 1;
389                     return -1;
390                 }
391                 LOG(log_debug7, logtype_cnid, "transmit: ... OK.");
392             } else { /* db->notfirst == 0 */
393                 db->notfirst = 1;
394                 if (db->client_stamp)
395                     memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
396                 memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
397             }
398             LOG(log_debug, logtype_cnid, "transmit: attached to '%s', stamp: '%08lx'.",
399                 db->db_dir, *(uint64_t *)stamp);
400         }
401         if (!dbd_rpc(db, rqst, rply)) {
402             LOG(log_maxdebug, logtype_cnid, "transmit: {done}");
403             return 0;
404         }
405     transmit_fail:
406         if (db->fd != -1) {
407             close(db->fd);
408             db->fd = -1; /* FD not valid... will need to reconnect */
409         }
410
411         if (errno == ECONNREFUSED) { /* errno carefully injected in tsock_getfd */
412             /* give up */
413             LOG(log_error, logtype_cnid, "transmit: connection refused (db_dir %s)", db->db_dir);
414             return -1;
415         }
416
417         if (!clean) { /* don't sleep if just got disconnected by cnid server */
418             time(&t);
419             if (t - orig > MAX_DELAY) {
420                 LOG(log_error, logtype_cnid, "transmit: Request to dbd daemon (db_dir %s) timed out.", db->db_dir);
421                 return -1;
422             }
423             /* sleep a little before retry */
424             delay(1);
425         } else {
426             clean = 0; /* false... next time sleep */
427             time(&orig);
428         }
429     }
430     return -1;
431 }
432
433 /* ---------------------- */
434 static struct _cnid_db *cnid_dbd_new(const char *volpath)
435 {
436     struct _cnid_db *cdb;
437
438     if ((cdb = (struct _cnid_db *)calloc(1, sizeof(struct _cnid_db))) == NULL)
439         return NULL;
440
441     if ((cdb->volpath = strdup(volpath)) == NULL) {
442         free(cdb);
443         return NULL;
444     }
445
446     cdb->flags = CNID_FLAG_PERSISTENT | CNID_FLAG_LAZY_INIT;
447
448     cdb->cnid_add = cnid_dbd_add;
449     cdb->cnid_delete = cnid_dbd_delete;
450     cdb->cnid_get = cnid_dbd_get;
451     cdb->cnid_lookup = cnid_dbd_lookup;
452     cdb->cnid_find = cnid_dbd_find;
453     cdb->cnid_nextid = NULL;
454     cdb->cnid_resolve = cnid_dbd_resolve;
455     cdb->cnid_getstamp = cnid_dbd_getstamp;
456     cdb->cnid_update = cnid_dbd_update;
457     cdb->cnid_rebuild_add = cnid_dbd_rebuild_add;
458     cdb->cnid_close = cnid_dbd_close;
459
460     return cdb;
461 }
462
463 /* ---------------------- */
464 struct _cnid_db *cnid_dbd_open(struct cnid_open_args *args)
465 {
466     CNID_private *db = NULL;
467     struct _cnid_db *cdb = NULL;
468
469     if (!args->dir) {
470         return NULL;
471     }
472
473     if ((cdb = cnid_dbd_new(args->dir)) == NULL) {
474         LOG(log_error, logtype_cnid, "cnid_open: Unable to allocate memory for database");
475         return NULL;
476     }
477
478     if ((db = (CNID_private *)calloc(1, sizeof(CNID_private))) == NULL) {
479         LOG(log_error, logtype_cnid, "cnid_open: Unable to allocate memory for database");
480         goto cnid_dbd_open_fail;
481     }
482
483     cdb->_private = db;
484
485     /* We keep a copy of the directory in the db structure so that we can
486        transparently reconnect later. */
487     strcpy(db->db_dir, args->dir);
488     db->magic = CNID_DB_MAGIC;
489     db->fd = -1;
490     db->cnidserver = strdup(args->cnidserver);
491     db->cnidport = strdup(args->cnidport);
492
493     LOG(log_debug, logtype_cnid, "cnid_dbd_open: Finished initializing cnid dbd module for volume '%s'", db->db_dir);
494
495     return cdb;
496
497 cnid_dbd_open_fail:
498     if (cdb != NULL) {
499         if (cdb->volpath != NULL) {
500             free(cdb->volpath);
501         }
502         free(cdb);
503     }
504     if (db != NULL)
505         free(db);
506
507     return NULL;
508 }
509
510 /* ---------------------- */
511 void cnid_dbd_close(struct _cnid_db *cdb)
512 {
513     CNID_private *db;
514
515     if (!cdb) {
516         LOG(log_error, logtype_cnid, "cnid_close called with NULL argument !");
517         return;
518     }
519
520     if ((db = cdb->_private) != NULL) {
521         LOG(log_debug, logtype_cnid, "closing database connection for volume '%s'", db->db_dir);
522
523         if (db->fd >= 0)
524             close(db->fd);
525         free(db);
526     }
527
528     free(cdb->volpath);
529     free(cdb);
530
531     return;
532 }
533
534 /* ---------------------- */
535 cnid_t cnid_dbd_add(struct _cnid_db *cdb, const struct stat *st,
536                     cnid_t did, const char *name, size_t len, cnid_t hint)
537 {
538     CNID_private *db;
539     struct cnid_dbd_rqst rqst;
540     struct cnid_dbd_rply rply;
541     cnid_t id;
542
543     if (!cdb || !(db = cdb->_private) || !st || !name) {
544         LOG(log_error, logtype_cnid, "cnid_add: Parameter error");
545         errno = CNID_ERR_PARAM;
546         return CNID_INVALID;
547     }
548
549     if (len > MAXPATHLEN) {
550         LOG(log_error, logtype_cnid, "cnid_add: Path name is too long");
551         errno = CNID_ERR_PATH;
552         return CNID_INVALID;
553     }
554
555     RQST_RESET(&rqst);
556     rqst.op = CNID_DBD_OP_ADD;
557
558     if (!(cdb->flags & CNID_FLAG_NODEV)) {
559         rqst.dev = st->st_dev;
560     }
561
562     rqst.ino = st->st_ino;
563     rqst.type = S_ISDIR(st->st_mode)?1:0;
564     rqst.cnid = hint;
565     rqst.did = did;
566     rqst.name = name;
567     rqst.namelen = len;
568
569     LOG(log_debug, logtype_cnid, "cnid_dbd_add: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir)",
570         ntohl(did), name, (long long)st->st_ino, rqst.type);
571
572     rply.namelen = 0;
573     if (transmit(db, &rqst, &rply) < 0) {
574         errno = CNID_ERR_DB;
575         return CNID_INVALID;
576     }
577
578     switch(rply.result) {
579     case CNID_DBD_RES_OK:
580         id = rply.cnid;
581         LOG(log_debug, logtype_cnid, "cnid_dbd_add: got CNID: %u", ntohl(id));
582         break;
583     case CNID_DBD_RES_ERR_MAX:
584         errno = CNID_ERR_MAX;
585         id = CNID_INVALID;
586         break;
587     case CNID_DBD_RES_ERR_DB:
588     case CNID_DBD_RES_ERR_DUPLCNID:
589         errno = CNID_ERR_DB;
590         id = CNID_INVALID;
591         break;
592     default:
593         abort();
594     }
595
596     return id;
597 }
598
599 /* ---------------------- */
600 cnid_t cnid_dbd_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t len)
601 {
602     CNID_private *db;
603     struct cnid_dbd_rqst rqst;
604     struct cnid_dbd_rply rply;
605     cnid_t id;
606
607     if (!cdb || !(db = cdb->_private) || !name) {
608         LOG(log_error, logtype_cnid, "cnid_dbd_get: Parameter error");
609         errno = CNID_ERR_PARAM;
610         return CNID_INVALID;
611     }
612
613     if (len > MAXPATHLEN) {
614         LOG(log_error, logtype_cnid, "cnid_dbd_get: Path name is too long");
615         errno = CNID_ERR_PATH;
616         return CNID_INVALID;
617     }
618
619     LOG(log_debug, logtype_cnid, "cnid_dbd_get: DID: %u, name: '%s'", ntohl(did), name);
620
621     RQST_RESET(&rqst);
622     rqst.op = CNID_DBD_OP_GET;
623     rqst.did = did;
624     rqst.name = name;
625     rqst.namelen = len;
626
627     rply.namelen = 0;
628     if (transmit(db, &rqst, &rply) < 0) {
629         errno = CNID_ERR_DB;
630         return CNID_INVALID;
631     }
632
633     switch(rply.result) {
634     case CNID_DBD_RES_OK:
635         id = rply.cnid;
636         LOG(log_debug, logtype_cnid, "cnid_dbd_get: got CNID: %u", ntohl(id));
637         break;
638     case CNID_DBD_RES_NOTFOUND:
639         id = CNID_INVALID;
640         break;
641     case CNID_DBD_RES_ERR_DB:
642         id = CNID_INVALID;
643         errno = CNID_ERR_DB;
644         break;
645     default:
646         abort();
647     }
648
649     return id;
650 }
651
652 /* ---------------------- */
653 char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
654 {
655     CNID_private *db;
656     struct cnid_dbd_rqst rqst;
657     struct cnid_dbd_rply rply;
658     char *name;
659
660     if (!cdb || !(db = cdb->_private) || !id || !(*id)) {
661         LOG(log_error, logtype_cnid, "cnid_resolve: Parameter error");
662         errno = CNID_ERR_PARAM;
663         return NULL;
664     }
665
666     LOG(log_debug, logtype_cnid, "cnid_dbd_resolve: resolving CNID: %u", ntohl(*id));
667
668     /* TODO: We should maybe also check len. At the moment we rely on the caller
669        to provide a buffer that is large enough for MAXPATHLEN plus
670        CNID_HEADER_LEN plus 1 byte, which is large enough for the maximum that
671        can come from the database. */
672
673     RQST_RESET(&rqst);
674     rqst.op = CNID_DBD_OP_RESOLVE;
675     rqst.cnid = *id;
676
677     /* Pass buffer to transmit so it can stuff the reply data there */
678     rply.name = (char *)buffer;
679     rply.namelen = len;
680
681     if (transmit(db, &rqst, &rply) < 0) {
682         errno = CNID_ERR_DB;
683         *id = CNID_INVALID;
684         return NULL;
685     }
686
687     switch (rply.result) {
688     case CNID_DBD_RES_OK:
689         *id = rply.did;
690         name = rply.name + CNID_NAME_OFS;
691         LOG(log_debug, logtype_cnid, "cnid_dbd_resolve: resolved did: %u, name: '%s'", ntohl(*id), name);
692         break;
693     case CNID_DBD_RES_NOTFOUND:
694         *id = CNID_INVALID;
695         name = NULL;
696         break;
697     case CNID_DBD_RES_ERR_DB:
698         errno = CNID_ERR_DB;
699         *id = CNID_INVALID;
700         name = NULL;
701         break;
702     default:
703         abort();
704     }
705
706     return name;
707 }
708
709 /* ---------------------- */
710 int cnid_dbd_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
711 {
712     CNID_private *db;
713
714     if (!cdb || !(db = cdb->_private) || len != ADEDLEN_PRIVSYN) {
715         LOG(log_error, logtype_cnid, "cnid_getstamp: Parameter error");
716         errno = CNID_ERR_PARAM;
717         return -1;
718     }
719     db->client_stamp = buffer;
720     db->stamp_size = len;
721     memset(buffer,0, len);
722     return 0;
723 }
724
725 /* ---------------------- */
726 cnid_t cnid_dbd_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
727                        const char *name, size_t len)
728 {
729     CNID_private *db;
730     struct cnid_dbd_rqst rqst;
731     struct cnid_dbd_rply rply;
732     cnid_t id;
733
734     if (!cdb || !(db = cdb->_private) || !st || !name) {
735         LOG(log_error, logtype_cnid, "cnid_lookup: Parameter error");
736         errno = CNID_ERR_PARAM;
737         return CNID_INVALID;
738     }
739
740     if (len > MAXPATHLEN) {
741         LOG(log_error, logtype_cnid, "cnid_lookup: Path name is too long");
742         errno = CNID_ERR_PATH;
743         return CNID_INVALID;
744     }
745
746     RQST_RESET(&rqst);
747     rqst.op = CNID_DBD_OP_LOOKUP;
748
749     if (!(cdb->flags & CNID_FLAG_NODEV)) {
750         rqst.dev = st->st_dev;
751     }
752
753     rqst.ino = st->st_ino;
754     rqst.type = S_ISDIR(st->st_mode)?1:0;
755     rqst.did = did;
756     rqst.name = name;
757     rqst.namelen = len;
758
759     LOG(log_debug, logtype_cnid, "cnid_dbd_lookup: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir)",
760         ntohl(did), name, (long long)st->st_ino, rqst.type);
761
762     rply.namelen = 0;
763     if (transmit(db, &rqst, &rply) < 0) {
764         errno = CNID_ERR_DB;
765         return CNID_INVALID;
766     }
767
768     switch (rply.result) {
769     case CNID_DBD_RES_OK:
770         id = rply.cnid;
771         LOG(log_debug, logtype_cnid, "cnid_dbd_lookup: got CNID: %u", ntohl(id));
772         break;
773     case CNID_DBD_RES_NOTFOUND:
774         id = CNID_INVALID;
775         break;
776     case CNID_DBD_RES_ERR_DB:
777         errno = CNID_ERR_DB;
778         id = CNID_INVALID;
779         break;
780     default:
781         abort();
782     }
783
784     return id;
785 }
786
787 /* ---------------------- */
788 int cnid_dbd_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
789 {
790     CNID_private *db;
791     struct cnid_dbd_rqst rqst;
792     struct cnid_dbd_rply rply;
793     int count;
794
795     if (!cdb || !(db = cdb->_private) || !name) {
796         LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
797         errno = CNID_ERR_PARAM;
798         return CNID_INVALID;
799     }
800
801     if (namelen > MAXPATHLEN) {
802         LOG(log_error, logtype_cnid, "cnid_find: Path name is too long");
803         errno = CNID_ERR_PATH;
804         return CNID_INVALID;
805     }
806
807     LOG(log_debug, logtype_cnid, "cnid_find(\"%s\")", name);
808
809     RQST_RESET(&rqst);
810     rqst.op = CNID_DBD_OP_SEARCH;
811
812     rqst.name = name;
813     rqst.namelen = namelen;
814
815     rply.name = buffer;
816     rply.namelen = buflen;
817
818     if (transmit(db, &rqst, &rply) < 0) {
819         errno = CNID_ERR_DB;
820         return CNID_INVALID;
821     }
822
823     switch (rply.result) {
824     case CNID_DBD_RES_OK:
825         count = rply.namelen / sizeof(cnid_t);
826         LOG(log_debug, logtype_cnid, "cnid_find: got %d matches", count);
827         break;
828     case CNID_DBD_RES_NOTFOUND:
829         count = 0;
830         break;
831     case CNID_DBD_RES_ERR_DB:
832         errno = CNID_ERR_DB;
833         count = -1;
834         break;
835     default:
836         abort();
837     }
838
839     return count;
840 }
841
842 /* ---------------------- */
843 int cnid_dbd_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
844                     cnid_t did, const char *name, size_t len)
845 {
846     CNID_private *db;
847     struct cnid_dbd_rqst rqst;
848     struct cnid_dbd_rply rply;
849
850     if (!cdb || !(db = cdb->_private) || !id || !st || !name) {
851         LOG(log_error, logtype_cnid, "cnid_update: Parameter error");
852         errno = CNID_ERR_PARAM;
853         return -1;
854     }
855
856     if (len > MAXPATHLEN) {
857         LOG(log_error, logtype_cnid, "cnid_update: Path name is too long");
858         errno = CNID_ERR_PATH;
859         return -1;
860     }
861
862     RQST_RESET(&rqst);
863     rqst.op = CNID_DBD_OP_UPDATE;
864     rqst.cnid = id;
865     if (!(cdb->flags & CNID_FLAG_NODEV)) {
866         rqst.dev = st->st_dev;
867     }
868     rqst.ino = st->st_ino;
869     rqst.type = S_ISDIR(st->st_mode)?1:0;
870     rqst.did = did;
871     rqst.name = name;
872     rqst.namelen = len;
873
874     LOG(log_debug, logtype_cnid, "cnid_dbd_update: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir)",
875         ntohl(id), name, (long long)st->st_ino, rqst.type);
876
877     rply.namelen = 0;
878     if (transmit(db, &rqst, &rply) < 0) {
879         errno = CNID_ERR_DB;
880         return -1;
881     }
882
883     switch (rply.result) {
884     case CNID_DBD_RES_OK:
885         LOG(log_debug, logtype_cnid, "cnid_dbd_update: updated");
886     case CNID_DBD_RES_NOTFOUND:
887         return 0;
888     case CNID_DBD_RES_ERR_DB:
889         errno = CNID_ERR_DB;
890         return -1;
891     default:
892         abort();
893     }
894 }
895
896 /* ---------------------- */
897 cnid_t cnid_dbd_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
898                             cnid_t did, const char *name, size_t len, cnid_t hint)
899 {
900     CNID_private *db;
901     struct cnid_dbd_rqst rqst;
902     struct cnid_dbd_rply rply;
903     cnid_t id;
904
905     if (!cdb || !(db = cdb->_private) || !st || !name || hint == CNID_INVALID) {
906         LOG(log_error, logtype_cnid, "cnid_rebuild_add: Parameter error");
907         errno = CNID_ERR_PARAM;
908         return CNID_INVALID;
909     }
910
911     if (len > MAXPATHLEN) {
912         LOG(log_error, logtype_cnid, "cnid_rebuild_add: Path name is too long");
913         errno = CNID_ERR_PATH;
914         return CNID_INVALID;
915     }
916
917     RQST_RESET(&rqst);
918     rqst.op = CNID_DBD_OP_REBUILD_ADD;
919
920     if (!(cdb->flags & CNID_FLAG_NODEV)) {
921         rqst.dev = st->st_dev;
922     }
923
924     rqst.ino = st->st_ino;
925     rqst.type = S_ISDIR(st->st_mode)?1:0;
926     rqst.did = did;
927     rqst.name = name;
928     rqst.namelen = len;
929     rqst.cnid = hint;
930
931     LOG(log_debug, logtype_cnid, "cnid_dbd_rebuild_add: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir), hint: %u",
932         ntohl(did), name, (long long)st->st_ino, rqst.type, hint);
933
934     if (transmit(db, &rqst, &rply) < 0) {
935         errno = CNID_ERR_DB;
936         return CNID_INVALID;
937     }
938
939     switch(rply.result) {
940     case CNID_DBD_RES_OK:
941         id = rply.cnid;
942         LOG(log_debug, logtype_cnid, "cnid_dbd_rebuild_add: got CNID: %u", ntohl(id));
943         break;
944     case CNID_DBD_RES_ERR_MAX:
945         errno = CNID_ERR_MAX;
946         id = CNID_INVALID;
947         break;
948     case CNID_DBD_RES_ERR_DB:
949     case CNID_DBD_RES_ERR_DUPLCNID:
950         errno = CNID_ERR_DB;
951         id = CNID_INVALID;
952         break;
953     default:
954         abort();
955     }
956     return id;
957 }
958
959 /* ---------------------- */
960 int cnid_dbd_delete(struct _cnid_db *cdb, const cnid_t id)
961 {
962     CNID_private *db;
963     struct cnid_dbd_rqst rqst;
964     struct cnid_dbd_rply rply;
965
966     if (!cdb || !(db = cdb->_private) || !id) {
967         LOG(log_error, logtype_cnid, "cnid_delete: Parameter error");
968         errno = CNID_ERR_PARAM;
969         return -1;
970     }
971
972     LOG(log_debug, logtype_cnid, "cnid_dbd_delete: delete CNID: %u", ntohl(id));
973
974     RQST_RESET(&rqst);
975     rqst.op = CNID_DBD_OP_DELETE;
976     rqst.cnid = id;
977
978     rply.namelen = 0;
979     if (transmit(db, &rqst, &rply) < 0) {
980         errno = CNID_ERR_DB;
981         return -1;
982     }
983
984     switch (rply.result) {
985     case CNID_DBD_RES_OK:
986         LOG(log_debug, logtype_cnid, "cnid_dbd_delete: deleted CNID: %u", ntohl(id));
987     case CNID_DBD_RES_NOTFOUND:
988         return 0;
989     case CNID_DBD_RES_ERR_DB:
990         errno = CNID_ERR_DB;
991         return -1;
992     default:
993         abort();
994     }
995 }
996
997
998 struct _cnid_module cnid_dbd_module = {
999     "dbd",
1000     {NULL, NULL},
1001     cnid_dbd_open,
1002     0
1003 };
1004
1005 #endif /* CNID_DBD */
1006