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