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