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