]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/dbd/cnid_dbd.c
1b365065cdd79194d2b8329fbef496bfa8ffdcf6
[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_bdb_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_bdb_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_bdb_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_bdb_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_bdb_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     while (1) {
360         if (db->fd == -1) {
361             LOG(log_maxdebug, logtype_cnid, "transmit: connecting to cnid_dbd ...");
362             if ((db->fd = init_tsock(db)) < 0) {
363                 goto transmit_fail;
364             }
365             if (db->notfirst) {
366                 LOG(log_debug7, logtype_cnid, "transmit: reconnected to cnid_dbd");
367             } else { /* db->notfirst == 0 */
368                 db->notfirst = 1;
369             }
370             LOG(log_debug, logtype_cnid, "transmit: attached to '%s'", db->db_dir);
371         }
372         if (!dbd_rpc(db, rqst, rply)) {
373             LOG(log_maxdebug, logtype_cnid, "transmit: {done}");
374             return 0;
375         }
376     transmit_fail:
377         if (db->fd != -1) {
378             close(db->fd);
379             db->fd = -1; /* FD not valid... will need to reconnect */
380         }
381
382         if (errno == ECONNREFUSED) { /* errno carefully injected in tsock_getfd */
383             /* give up */
384             LOG(log_error, logtype_cnid, "transmit: connection refused (db_dir %s)", db->db_dir);
385             return -1;
386         }
387
388         if (!clean) { /* don't sleep if just got disconnected by cnid server */
389             time(&t);
390             if (t - orig > MAX_DELAY) {
391                 LOG(log_error, logtype_cnid, "transmit: Request to dbd daemon (db_dir %s) timed out.", db->db_dir);
392                 return -1;
393             }
394             /* sleep a little before retry */
395             delay(1);
396         } else {
397             clean = 0; /* false... next time sleep */
398             time(&orig);
399         }
400     }
401     return -1;
402 }
403
404 /* ---------------------- */
405 static struct _cnid_db *cnid_dbd_new(const char *volpath)
406 {
407     struct _cnid_db *cdb;
408
409     if ((cdb = (struct _cnid_db *)calloc(1, sizeof(struct _cnid_db))) == NULL)
410         return NULL;
411
412     if ((cdb->volpath = strdup(volpath)) == NULL) {
413         free(cdb);
414         return NULL;
415     }
416
417     cdb->flags = CNID_FLAG_PERSISTENT | CNID_FLAG_LAZY_INIT;
418
419     cdb->cnid_add = cnid_dbd_add;
420     cdb->cnid_delete = cnid_dbd_delete;
421     cdb->cnid_get = cnid_dbd_get;
422     cdb->cnid_lookup = cnid_dbd_lookup;
423     cdb->cnid_find = cnid_dbd_find;
424     cdb->cnid_nextid = NULL;
425     cdb->cnid_resolve = cnid_dbd_resolve;
426     cdb->cnid_getstamp = cnid_dbd_getstamp;
427     cdb->cnid_update = cnid_dbd_update;
428     cdb->cnid_rebuild_add = cnid_dbd_rebuild_add;
429     cdb->cnid_close = cnid_dbd_close;
430     cdb->cnid_wipe = cnid_dbd_wipe;
431     return cdb;
432 }
433
434 /* ---------------------- */
435 struct _cnid_db *cnid_dbd_open(struct cnid_open_args *args)
436 {
437     CNID_bdb_private *db = NULL;
438     struct _cnid_db *cdb = NULL;
439
440     if (!args->dir) {
441         return NULL;
442     }
443
444     if ((cdb = cnid_dbd_new(args->dir)) == NULL) {
445         LOG(log_error, logtype_cnid, "cnid_open: Unable to allocate memory for database");
446         return NULL;
447     }
448
449     if ((db = (CNID_bdb_private *)calloc(1, sizeof(CNID_bdb_private))) == NULL) {
450         LOG(log_error, logtype_cnid, "cnid_open: Unable to allocate memory for database");
451         goto cnid_dbd_open_fail;
452     }
453
454     cdb->_private = db;
455
456     /* We keep a copy of the directory in the db structure so that we can
457        transparently reconnect later. */
458     strcpy(db->db_dir, args->dir);
459     db->magic = CNID_DB_MAGIC;
460     db->fd = -1;
461     db->cnidserver = strdup(args->cnidserver);
462     db->cnidport = strdup(args->cnidport);
463
464     LOG(log_debug, logtype_cnid, "cnid_dbd_open: Finished initializing cnid dbd module for volume '%s'", db->db_dir);
465
466     return cdb;
467
468 cnid_dbd_open_fail:
469     if (cdb != NULL) {
470         if (cdb->volpath != NULL) {
471             free(cdb->volpath);
472         }
473         free(cdb);
474     }
475     if (db != NULL)
476         free(db);
477
478     return NULL;
479 }
480
481 /* ---------------------- */
482 void cnid_dbd_close(struct _cnid_db *cdb)
483 {
484     CNID_bdb_private *db;
485
486     if (!cdb) {
487         LOG(log_error, logtype_cnid, "cnid_close called with NULL argument !");
488         return;
489     }
490
491     if ((db = cdb->_private) != NULL) {
492         LOG(log_debug, logtype_cnid, "closing database connection for volume '%s'", db->db_dir);
493
494         if (db->fd >= 0)
495             close(db->fd);
496         free(db);
497     }
498
499     free(cdb->volpath);
500     free(cdb);
501
502     return;
503 }
504
505 /**
506  * Get the db stamp
507  **/
508 static int cnid_dbd_stamp(CNID_bdb_private *db)
509 {
510     struct cnid_dbd_rqst rqst_stamp;
511     struct cnid_dbd_rply rply_stamp;
512     char  stamp[ADEDLEN_PRIVSYN];
513
514     dbd_initstamp(&rqst_stamp);
515     memset(stamp, 0, ADEDLEN_PRIVSYN);
516     rply_stamp.name = stamp;
517     rply_stamp.namelen = ADEDLEN_PRIVSYN;
518
519     if (transmit(db, &rqst_stamp, &rply_stamp) < 0)
520         return -1;
521     if (dbd_reply_stamp(&rply_stamp ) < 0)
522         return -1;
523
524     if (db->client_stamp)
525         memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
526     memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
527
528     return 0;
529 }
530
531 /* ---------------------- */
532 cnid_t cnid_dbd_add(struct _cnid_db *cdb, const struct stat *st,
533                     cnid_t did, const char *name, size_t len, cnid_t hint)
534 {
535     CNID_bdb_private *db;
536     struct cnid_dbd_rqst rqst;
537     struct cnid_dbd_rply rply;
538     cnid_t id;
539
540     if (!cdb || !(db = cdb->_private) || !st || !name) {
541         LOG(log_error, logtype_cnid, "cnid_add: Parameter error");
542         errno = CNID_ERR_PARAM;
543         return CNID_INVALID;
544     }
545
546     if (len > MAXPATHLEN) {
547         LOG(log_error, logtype_cnid, "cnid_add: Path name is too long");
548         errno = CNID_ERR_PATH;
549         return CNID_INVALID;
550     }
551
552     RQST_RESET(&rqst);
553     rqst.op = CNID_DBD_OP_ADD;
554
555     if (!(cdb->flags & CNID_FLAG_NODEV)) {
556         rqst.dev = st->st_dev;
557     }
558
559     rqst.ino = st->st_ino;
560     rqst.type = S_ISDIR(st->st_mode)?1:0;
561     rqst.cnid = hint;
562     rqst.did = did;
563     rqst.name = name;
564     rqst.namelen = len;
565
566     LOG(log_debug, logtype_cnid, "cnid_dbd_add: CNID: %u, name: '%s', dev: 0x%llx, inode: 0x%llx, type: %s",
567         ntohl(did), name, (long long)rqst.dev, (long long)st->st_ino, rqst.type ? "dir" : "file");
568
569     rply.namelen = 0;
570     if (transmit(db, &rqst, &rply) < 0) {
571         errno = CNID_ERR_DB;
572         return CNID_INVALID;
573     }
574
575     switch(rply.result) {
576     case CNID_DBD_RES_OK:
577         id = rply.cnid;
578         LOG(log_debug, logtype_cnid, "cnid_dbd_add: got CNID: %u", ntohl(id));
579         break;
580     case CNID_DBD_RES_ERR_MAX:
581         errno = CNID_ERR_MAX;
582         id = CNID_INVALID;
583         break;
584     case CNID_DBD_RES_ERR_DB:
585     case CNID_DBD_RES_ERR_DUPLCNID:
586         errno = CNID_ERR_DB;
587         id = CNID_INVALID;
588         break;
589     default:
590         abort();
591     }
592
593     return id;
594 }
595
596 /* ---------------------- */
597 cnid_t cnid_dbd_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t len)
598 {
599     CNID_bdb_private *db;
600     struct cnid_dbd_rqst rqst;
601     struct cnid_dbd_rply rply;
602     cnid_t id;
603
604     if (!cdb || !(db = cdb->_private) || !name) {
605         LOG(log_error, logtype_cnid, "cnid_dbd_get: Parameter error");
606         errno = CNID_ERR_PARAM;
607         return CNID_INVALID;
608     }
609
610     if (len > MAXPATHLEN) {
611         LOG(log_error, logtype_cnid, "cnid_dbd_get: Path name is too long");
612         errno = CNID_ERR_PATH;
613         return CNID_INVALID;
614     }
615
616     LOG(log_debug, logtype_cnid, "cnid_dbd_get: DID: %u, name: '%s'", ntohl(did), name);
617
618     RQST_RESET(&rqst);
619     rqst.op = CNID_DBD_OP_GET;
620     rqst.did = did;
621     rqst.name = name;
622     rqst.namelen = len;
623
624     rply.namelen = 0;
625     if (transmit(db, &rqst, &rply) < 0) {
626         errno = CNID_ERR_DB;
627         return CNID_INVALID;
628     }
629
630     switch(rply.result) {
631     case CNID_DBD_RES_OK:
632         id = rply.cnid;
633         LOG(log_debug, logtype_cnid, "cnid_dbd_get: got CNID: %u", ntohl(id));
634         break;
635     case CNID_DBD_RES_NOTFOUND:
636         id = CNID_INVALID;
637         break;
638     case CNID_DBD_RES_ERR_DB:
639         id = CNID_INVALID;
640         errno = CNID_ERR_DB;
641         break;
642     default:
643         abort();
644     }
645
646     return id;
647 }
648
649 /* ---------------------- */
650 char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
651 {
652     CNID_bdb_private *db;
653     struct cnid_dbd_rqst rqst;
654     struct cnid_dbd_rply rply;
655     char *name;
656
657     if (!cdb || !(db = cdb->_private) || !id || !(*id)) {
658         LOG(log_error, logtype_cnid, "cnid_resolve: Parameter error");
659         errno = CNID_ERR_PARAM;
660         return NULL;
661     }
662
663     LOG(log_debug, logtype_cnid, "cnid_dbd_resolve: resolving CNID: %u", ntohl(*id));
664
665     /* TODO: We should maybe also check len. At the moment we rely on the caller
666        to provide a buffer that is large enough for MAXPATHLEN plus
667        CNID_HEADER_LEN plus 1 byte, which is large enough for the maximum that
668        can come from the database. */
669
670     RQST_RESET(&rqst);
671     rqst.op = CNID_DBD_OP_RESOLVE;
672     rqst.cnid = *id;
673
674     /* Pass buffer to transmit so it can stuff the reply data there */
675     rply.name = (char *)buffer;
676     rply.namelen = len;
677
678     if (transmit(db, &rqst, &rply) < 0) {
679         errno = CNID_ERR_DB;
680         *id = CNID_INVALID;
681         return NULL;
682     }
683
684     switch (rply.result) {
685     case CNID_DBD_RES_OK:
686         *id = rply.did;
687         name = rply.name + CNID_NAME_OFS;
688         LOG(log_debug, logtype_cnid, "cnid_dbd_resolve: resolved did: %u, name: '%s'", ntohl(*id), name);
689         break;
690     case CNID_DBD_RES_NOTFOUND:
691         *id = CNID_INVALID;
692         name = NULL;
693         break;
694     case CNID_DBD_RES_ERR_DB:
695         errno = CNID_ERR_DB;
696         *id = CNID_INVALID;
697         name = NULL;
698         break;
699     default:
700         abort();
701     }
702
703     return name;
704 }
705
706 /**
707  * Caller passes buffer where we will store the db stamp
708  **/
709 int cnid_dbd_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
710 {
711     CNID_bdb_private *db;
712
713     if (!cdb || !(db = cdb->_private) || len != ADEDLEN_PRIVSYN) {
714         LOG(log_error, logtype_cnid, "cnid_getstamp: Parameter error");
715         errno = CNID_ERR_PARAM;
716         return -1;
717     }
718     db->client_stamp = buffer;
719     db->stamp_size = len;
720
721     return cnid_dbd_stamp(db);
722 }
723
724 /* ---------------------- */
725 cnid_t cnid_dbd_lookup(struct _cnid_db *cdb, const struct stat *st, cnid_t did,
726                        const char *name, size_t len)
727 {
728     CNID_bdb_private *db;
729     struct cnid_dbd_rqst rqst;
730     struct cnid_dbd_rply rply;
731     cnid_t id;
732
733     if (!cdb || !(db = cdb->_private) || !st || !name) {
734         LOG(log_error, logtype_cnid, "cnid_lookup: Parameter error");
735         errno = CNID_ERR_PARAM;
736         return CNID_INVALID;
737     }
738
739     if (len > MAXPATHLEN) {
740         LOG(log_error, logtype_cnid, "cnid_lookup: Path name is too long");
741         errno = CNID_ERR_PATH;
742         return CNID_INVALID;
743     }
744
745     RQST_RESET(&rqst);
746     rqst.op = CNID_DBD_OP_LOOKUP;
747
748     if (!(cdb->flags & CNID_FLAG_NODEV)) {
749         rqst.dev = st->st_dev;
750     }
751
752     rqst.ino = st->st_ino;
753     rqst.type = S_ISDIR(st->st_mode)?1:0;
754     rqst.did = did;
755     rqst.name = name;
756     rqst.namelen = len;
757
758     LOG(log_debug, logtype_cnid, "cnid_dbd_lookup: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir)",
759         ntohl(did), name, (long long)st->st_ino, rqst.type);
760
761     rply.namelen = 0;
762     if (transmit(db, &rqst, &rply) < 0) {
763         errno = CNID_ERR_DB;
764         return CNID_INVALID;
765     }
766
767     switch (rply.result) {
768     case CNID_DBD_RES_OK:
769         id = rply.cnid;
770         LOG(log_debug, logtype_cnid, "cnid_dbd_lookup: got CNID: %u", ntohl(id));
771         break;
772     case CNID_DBD_RES_NOTFOUND:
773         id = CNID_INVALID;
774         break;
775     case CNID_DBD_RES_ERR_DB:
776         errno = CNID_ERR_DB;
777         id = CNID_INVALID;
778         break;
779     default:
780         abort();
781     }
782
783     return id;
784 }
785
786 /* ---------------------- */
787 int cnid_dbd_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
788 {
789     CNID_bdb_private *db;
790     struct cnid_dbd_rqst rqst;
791     struct cnid_dbd_rply rply;
792     int count;
793
794     if (!cdb || !(db = cdb->_private) || !name) {
795         LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
796         errno = CNID_ERR_PARAM;
797         return CNID_INVALID;
798     }
799
800     if (namelen > MAXPATHLEN) {
801         LOG(log_error, logtype_cnid, "cnid_find: Path name is too long");
802         errno = CNID_ERR_PATH;
803         return CNID_INVALID;
804     }
805
806     LOG(log_debug, logtype_cnid, "cnid_find(\"%s\")", name);
807
808     RQST_RESET(&rqst);
809     rqst.op = CNID_DBD_OP_SEARCH;
810
811     rqst.name = name;
812     rqst.namelen = namelen;
813
814     rply.name = buffer;
815     rply.namelen = buflen;
816
817     if (transmit(db, &rqst, &rply) < 0) {
818         errno = CNID_ERR_DB;
819         return CNID_INVALID;
820     }
821
822     switch (rply.result) {
823     case CNID_DBD_RES_OK:
824         count = rply.namelen / sizeof(cnid_t);
825         LOG(log_debug, logtype_cnid, "cnid_find: got %d matches", count);
826         break;
827     case CNID_DBD_RES_NOTFOUND:
828         count = 0;
829         break;
830     case CNID_DBD_RES_ERR_DB:
831         errno = CNID_ERR_DB;
832         count = -1;
833         break;
834     default:
835         abort();
836     }
837
838     return count;
839 }
840
841 /* ---------------------- */
842 int cnid_dbd_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
843                     cnid_t did, const char *name, size_t len)
844 {
845     CNID_bdb_private *db;
846     struct cnid_dbd_rqst rqst;
847     struct cnid_dbd_rply rply;
848
849     if (!cdb || !(db = cdb->_private) || !id || !st || !name) {
850         LOG(log_error, logtype_cnid, "cnid_update: Parameter error");
851         errno = CNID_ERR_PARAM;
852         return -1;
853     }
854
855     if (len > MAXPATHLEN) {
856         LOG(log_error, logtype_cnid, "cnid_update: Path name is too long");
857         errno = CNID_ERR_PATH;
858         return -1;
859     }
860
861     RQST_RESET(&rqst);
862     rqst.op = CNID_DBD_OP_UPDATE;
863     rqst.cnid = id;
864     if (!(cdb->flags & CNID_FLAG_NODEV)) {
865         rqst.dev = st->st_dev;
866     }
867     rqst.ino = st->st_ino;
868     rqst.type = S_ISDIR(st->st_mode)?1:0;
869     rqst.did = did;
870     rqst.name = name;
871     rqst.namelen = len;
872
873     LOG(log_debug, logtype_cnid, "cnid_dbd_update: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir)",
874         ntohl(id), name, (long long)st->st_ino, rqst.type);
875
876     rply.namelen = 0;
877     if (transmit(db, &rqst, &rply) < 0) {
878         errno = CNID_ERR_DB;
879         return -1;
880     }
881
882     switch (rply.result) {
883     case CNID_DBD_RES_OK:
884         LOG(log_debug, logtype_cnid, "cnid_dbd_update: updated");
885     case CNID_DBD_RES_NOTFOUND:
886         return 0;
887     case CNID_DBD_RES_ERR_DB:
888         errno = CNID_ERR_DB;
889         return -1;
890     default:
891         abort();
892     }
893 }
894
895 /* ---------------------- */
896 cnid_t cnid_dbd_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
897                             cnid_t did, const char *name, size_t len, cnid_t hint)
898 {
899     CNID_bdb_private *db;
900     struct cnid_dbd_rqst rqst;
901     struct cnid_dbd_rply rply;
902     cnid_t id;
903
904     if (!cdb || !(db = cdb->_private) || !st || !name || hint == CNID_INVALID) {
905         LOG(log_error, logtype_cnid, "cnid_rebuild_add: Parameter error");
906         errno = CNID_ERR_PARAM;
907         return CNID_INVALID;
908     }
909
910     if (len > MAXPATHLEN) {
911         LOG(log_error, logtype_cnid, "cnid_rebuild_add: Path name is too long");
912         errno = CNID_ERR_PATH;
913         return CNID_INVALID;
914     }
915
916     RQST_RESET(&rqst);
917     rqst.op = CNID_DBD_OP_REBUILD_ADD;
918
919     if (!(cdb->flags & CNID_FLAG_NODEV)) {
920         rqst.dev = st->st_dev;
921     }
922
923     rqst.ino = st->st_ino;
924     rqst.type = S_ISDIR(st->st_mode)?1:0;
925     rqst.did = did;
926     rqst.name = name;
927     rqst.namelen = len;
928     rqst.cnid = hint;
929
930     LOG(log_debug, logtype_cnid, "cnid_dbd_rebuild_add: CNID: %u, name: '%s', inode: 0x%llx, type: %d (0=file, 1=dir), hint: %u",
931         ntohl(did), name, (long long)st->st_ino, rqst.type, hint);
932
933     if (transmit(db, &rqst, &rply) < 0) {
934         errno = CNID_ERR_DB;
935         return CNID_INVALID;
936     }
937
938     switch(rply.result) {
939     case CNID_DBD_RES_OK:
940         id = rply.cnid;
941         LOG(log_debug, logtype_cnid, "cnid_dbd_rebuild_add: got CNID: %u", ntohl(id));
942         break;
943     case CNID_DBD_RES_ERR_MAX:
944         errno = CNID_ERR_MAX;
945         id = CNID_INVALID;
946         break;
947     case CNID_DBD_RES_ERR_DB:
948     case CNID_DBD_RES_ERR_DUPLCNID:
949         errno = CNID_ERR_DB;
950         id = CNID_INVALID;
951         break;
952     default:
953         abort();
954     }
955     return id;
956 }
957
958 /* ---------------------- */
959 int cnid_dbd_delete(struct _cnid_db *cdb, const cnid_t id)
960 {
961     CNID_bdb_private *db;
962     struct cnid_dbd_rqst rqst;
963     struct cnid_dbd_rply rply;
964
965     if (!cdb || !(db = cdb->_private) || !id) {
966         LOG(log_error, logtype_cnid, "cnid_delete: Parameter error");
967         errno = CNID_ERR_PARAM;
968         return -1;
969     }
970
971     LOG(log_debug, logtype_cnid, "cnid_dbd_delete: delete CNID: %u", ntohl(id));
972
973     RQST_RESET(&rqst);
974     rqst.op = CNID_DBD_OP_DELETE;
975     rqst.cnid = id;
976
977     rply.namelen = 0;
978     if (transmit(db, &rqst, &rply) < 0) {
979         errno = CNID_ERR_DB;
980         return -1;
981     }
982
983     switch (rply.result) {
984     case CNID_DBD_RES_OK:
985         LOG(log_debug, logtype_cnid, "cnid_dbd_delete: deleted CNID: %u", ntohl(id));
986     case CNID_DBD_RES_NOTFOUND:
987         return 0;
988     case CNID_DBD_RES_ERR_DB:
989         errno = CNID_ERR_DB;
990         return -1;
991     default:
992         abort();
993     }
994 }
995
996 int cnid_dbd_wipe(struct _cnid_db *cdb)
997 {
998     CNID_bdb_private *db;
999     struct cnid_dbd_rqst rqst;
1000     struct cnid_dbd_rply rply;
1001
1002     if (!cdb || !(db = cdb->_private)) {
1003         LOG(log_error, logtype_cnid, "cnid_wipe: Parameter error");
1004         errno = CNID_ERR_PARAM;
1005         return -1;
1006     }
1007
1008     LOG(log_debug, logtype_cnid, "cnid_dbd_wipe");
1009
1010     RQST_RESET(&rqst);
1011     rqst.op = CNID_DBD_OP_WIPE;
1012     rqst.cnid = 0;
1013
1014     rply.namelen = 0;
1015     if (transmit(db, &rqst, &rply) < 0) {
1016         errno = CNID_ERR_DB;
1017         return -1;
1018     }
1019
1020     if (rply.result != CNID_DBD_RES_OK) {
1021         errno = CNID_ERR_DB;
1022         return -1;
1023     }
1024     LOG(log_debug, logtype_cnid, "cnid_dbd_wipe: ok");
1025
1026     return cnid_dbd_stamp(db);
1027 }
1028
1029
1030 struct _cnid_module cnid_dbd_module = {
1031     "dbd",
1032     {NULL, NULL},
1033     cnid_dbd_open,
1034     0
1035 };
1036
1037 #endif /* CNID_DBD */
1038