+/* ---------------------------- */
+struct dir *
+adddir(struct vol *vol, struct dir *dir, struct path *path)
+{
+ struct dir *cdir, *edir;
+ int upathlen;
+ char *name;
+ char *upath;
+ struct stat *st;
+ int deleted;
+ struct adouble ad;
+ struct adouble *adp = NULL;
+ cnid_t id;
+
+ upath = path->u_name;
+ st = &path->st;
+ upathlen = strlen(upath);
+
+ /* get_id needs adp for reading CNID from adouble file */
+ ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+ if ((ad_open_metadata(upath, ADFLAGS_DIR, 0, &ad)) == 0)
+ adp = &ad;
+
+ id = get_id(vol, adp, st, dir->d_did, upath, upathlen);
+
+ if (adp)
+ ad_close_metadata(adp);
+
+ if (id == 0) {
+ return NULL;
+ }
+ if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
+ return NULL;
+ }
+ name = path->m_name;
+ if ((cdir = dirnew(name, upath)) == NULL) {
+ LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
+ return NULL;
+ }
+ if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, -1, (char **)&cdir->d_m_name_ucs2)) {
+ LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
+ cdir->d_m_name_ucs2 = NULL;
+ }
+
+ cdir->d_did = id;
+
+ if ((edir = dirinsert( vol, cdir ))) {
+ /* it's not possible with LASTDID
+ for CNID:
+ - someone else have moved the directory.
+ - it's a symlink inside the share.
+ - it's an ID reused, the old directory was deleted but not
+ the cnid record and the server've reused the inode for
+ the new dir.
+ for HASH (we should get ride of HASH)
+ - someone else have moved the directory.
+ - it's an ID reused as above
+ - it's a hash duplicate and we are in big trouble
+ */
+ deleted = (edir->d_m_name == NULL);
+ if (!deleted)
+ dir_hash_del(vol, edir);
+ dirfreename(edir);
+ edir->d_m_name = cdir->d_m_name;
+ edir->d_u_name = cdir->d_u_name;
+ edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
+ free(cdir);
+ cdir = edir;
+ LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
+ if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
+ hash_alloc_insert(vol->v_hash, cdir, cdir);
+ return cdir;
+ }
+ /* the old was not in the same folder */
+ if (!deleted)
+ dirchildremove(cdir->d_parent, cdir);
+ }
+
+ /* parent/child directories */
+ cdir->d_parent = dir;
+ dirchildadd(vol, dir, cdir);
+ return( cdir );
+}
+
+/* --- public functions follow --- */