Fix bug #7781 (Samba transforms "ShareName" to lowercase when adding new share via...
[samba.git] / source3 / modules / vfs_xattr_tdb.c
index 8cf04d79ad935ae4f60b2beffab368372316ed8e..fa8db047f8cf6b03f7fd6e98384519f9c2612185 100644 (file)
@@ -28,9 +28,9 @@
  * unmarshall tdb_xattrs
  */
 
-static NTSTATUS ea_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
-                                 const TDB_DATA *data,
-                                 struct tdb_xattrs **presult)
+static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
+                                    const TDB_DATA *data,
+                                    struct tdb_xattrs **presult)
 {
        DATA_BLOB blob;
        enum ndr_err_code ndr_err;
@@ -48,7 +48,7 @@ static NTSTATUS ea_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
        blob = data_blob_const(data->dptr, data->dsize);
 
        ndr_err = ndr_pull_struct_blob(
-               &blob, result, result,
+               &blob, result, NULL, result,
                (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs);
 
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -66,15 +66,15 @@ static NTSTATUS ea_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
  * marshall tdb_xattrs
  */
 
-static NTSTATUS ea_tdb_push_attrs(TALLOC_CTX *mem_ctx,
-                                 const struct tdb_xattrs *attribs,
-                                 TDB_DATA *data)
+static NTSTATUS xattr_tdb_push_attrs(TALLOC_CTX *mem_ctx,
+                                    const struct tdb_xattrs *attribs,
+                                    TDB_DATA *data)
 {
        DATA_BLOB blob;
        enum ndr_err_code ndr_err;
 
        ndr_err = ndr_push_struct_blob(
-               &blob, mem_ctx, attribs,
+               &blob, mem_ctx, NULL, attribs,
                (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
 
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -91,15 +91,16 @@ static NTSTATUS ea_tdb_push_attrs(TALLOC_CTX *mem_ctx,
  * Load tdb_xattrs for a file from the tdb
  */
 
-static NTSTATUS ea_tdb_load_attrs(TALLOC_CTX *mem_ctx,
-                                 struct db_context *db_ctx,
-                                 const struct file_id *id,
-                                 struct tdb_xattrs **presult)
+static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
+                                    struct db_context *db_ctx,
+                                    const struct file_id *id,
+                                    struct tdb_xattrs **presult)
 {
        uint8 id_buf[16];
        NTSTATUS status;
        TDB_DATA data;
 
+       /* For backwards compatibility only store the dev/inode. */
        push_file_id_16((char *)id_buf, id);
 
        if (db_ctx->fetch(db_ctx, mem_ctx,
@@ -108,20 +109,22 @@ static NTSTATUS ea_tdb_load_attrs(TALLOC_CTX *mem_ctx,
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
-       status = ea_tdb_pull_attrs(mem_ctx, &data, presult);
+       status = xattr_tdb_pull_attrs(mem_ctx, &data, presult);
        TALLOC_FREE(data.dptr);
-       return NT_STATUS_OK;
+       return status;
 }
 
 /*
  * fetch_lock the tdb_ea record for a file
  */
 
-static struct db_record *ea_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
-                                          struct db_context *db_ctx,
-                                          const struct file_id *id)
+static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
+                                             struct db_context *db_ctx,
+                                             const struct file_id *id)
 {
        uint8 id_buf[16];
+
+       /* For backwards compatibility only store the dev/inode. */
        push_file_id_16((char *)id_buf, id);
        return db_ctx->fetch_locked(db_ctx, mem_ctx,
                                    make_tdb_data(id_buf, sizeof(id_buf)));
@@ -131,16 +134,16 @@ static struct db_record *ea_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
  * Save tdb_xattrs to a previously fetch_locked record
  */
 
-static NTSTATUS ea_tdb_save_attrs(struct db_record *rec,
-                                 const struct tdb_xattrs *attribs)
+static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
+                                    const struct tdb_xattrs *attribs)
 {
-       TDB_DATA data;
+       TDB_DATA data = tdb_null;
        NTSTATUS status;
 
-       status = ea_tdb_push_attrs(talloc_tos(), attribs, &data);
+       status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("ea_tdb_push_attrs failed: %s\n",
+               DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
                          nt_errstr(status)));
                return status;
        }
@@ -156,52 +159,55 @@ static NTSTATUS ea_tdb_save_attrs(struct db_record *rec,
  * Worker routine for getxattr and fgetxattr
  */
 
-static ssize_t ea_tdb_getattr(struct db_context *db_ctx,
-                             const struct file_id *id,
-                             const char *name, void *value, size_t size)
+static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
+                                const struct file_id *id,
+                                const char *name, void *value, size_t size)
 {
        struct tdb_xattrs *attribs;
        uint32_t i;
        ssize_t result = -1;
        NTSTATUS status;
 
-       status = ea_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
+       DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
+                  file_id_string_tos(id), name));
+
+       status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
+               DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
                           nt_errstr(status)));
                errno = EINVAL;
                return -1;
        }
 
-       for (i=0; i<attribs->num_xattrs; i++) {
-               if (strcmp(attribs->xattrs[i].name, name) == 0) {
+       for (i=0; i<attribs->num_eas; i++) {
+               if (strcmp(attribs->eas[i].name, name) == 0) {
                        break;
                }
        }
 
-       if (i == attribs->num_xattrs) {
+       if (i == attribs->num_eas) {
                errno = ENOATTR;
                goto fail;
        }
 
-       if (attribs->xattrs[i].value.length > size) {
+       if (attribs->eas[i].value.length > size) {
                errno = ERANGE;
                goto fail;
        }
 
-       memcpy(value, attribs->xattrs[i].value.data,
-              attribs->xattrs[i].value.length);
-       result = attribs->xattrs[i].value.length;
+       memcpy(value, attribs->eas[i].value.data,
+              attribs->eas[i].value.length);
+       result = attribs->eas[i].value.length;
 
  fail:
        TALLOC_FREE(attribs);
        return result;
 }
 
-static ssize_t ea_tdb_getxattr(struct vfs_handle_struct *handle,
-                              const char *path, const char *name,
-                              void *value, size_t size)
+static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
+                                 const char *path, const char *name,
+                                 void *value, size_t size)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -209,18 +215,18 @@ static ssize_t ea_tdb_getxattr(struct vfs_handle_struct *handle,
 
        SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
 
-       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+       if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       return ea_tdb_getattr(db, &id, name, value, size);
+       return xattr_tdb_getattr(db, &id, name, value, size);
 }
 
-static ssize_t ea_tdb_fgetxattr(struct vfs_handle_struct *handle,
-                               struct files_struct *fsp,
-                               const char *name, void *value, size_t size)
+static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
+                                  struct files_struct *fsp,
+                                  const char *name, void *value, size_t size)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -232,53 +238,67 @@ static ssize_t ea_tdb_fgetxattr(struct vfs_handle_struct *handle,
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       return ea_tdb_getattr(db, &id, name, value, size);
+       return xattr_tdb_getattr(db, &id, name, value, size);
 }
 
 /*
  * Worker routine for setxattr and fsetxattr
  */
 
-static int ea_tdb_setattr(struct db_context *db_ctx,
-                         const struct file_id *id, const char *name,
-                         const void *value, size_t size, int flags)
+static int xattr_tdb_setattr(struct db_context *db_ctx,
+                            const struct file_id *id, const char *name,
+                            const void *value, size_t size, int flags)
 {
        NTSTATUS status;
        struct db_record *rec;
        struct tdb_xattrs *attribs;
        uint32_t i;
 
-       rec = ea_tdb_lock_attrs(talloc_tos(), db_ctx, id);
+       DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
+                  file_id_string_tos(id), name));
+
+       rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
 
        if (rec == NULL) {
-               DEBUG(0, ("ea_tdb_lock_attrs failed\n"));
+               DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
                errno = EINVAL;
                return -1;
        }
 
-       status = ea_tdb_pull_attrs(rec, &rec->value, &attribs);
+       status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
+               DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
                           nt_errstr(status)));
                TALLOC_FREE(rec);
                return -1;
        }
 
-       for (i=0; i<attribs->num_xattrs; i++) {
-               if (strcmp(attribs->xattrs[i].name, name) == 0) {
+       for (i=0; i<attribs->num_eas; i++) {
+               if (strcmp(attribs->eas[i].name, name) == 0) {
+                       if (flags & XATTR_CREATE) {
+                               TALLOC_FREE(rec);
+                               errno = EEXIST;
+                               return -1;
+                       }
                        break;
                }
        }
 
-       if (i == attribs->num_xattrs) {
-               struct tdb_xattr *tmp;
+       if (i == attribs->num_eas) {
+               struct xattr_EA *tmp;
+
+               if (flags & XATTR_REPLACE) {
+                       TALLOC_FREE(rec);
+                       errno = ENOATTR;
+                       return -1;
+               }
 
                tmp = TALLOC_REALLOC_ARRAY(
-                       attribs, attribs->xattrs, struct tdb_xattr,
-                       attribs->num_xattrs + 1);
+                       attribs, attribs->eas, struct xattr_EA,
+                       attribs->num_eas+ 1);
 
                if (tmp == NULL) {
                        DEBUG(0, ("TALLOC_REALLOC_ARRAY failed\n"));
@@ -287,15 +307,15 @@ static int ea_tdb_setattr(struct db_context *db_ctx,
                        return -1;
                }
 
-               attribs->xattrs = tmp;
-               attribs->num_xattrs += 1;
+               attribs->eas = tmp;
+               attribs->num_eas += 1;
        }
 
-       attribs->xattrs[i].name = name;
-       attribs->xattrs[i].value.data = CONST_DISCARD(uint8 *, value);
-       attribs->xattrs[i].value.length = size;
+       attribs->eas[i].name = name;
+       attribs->eas[i].value.data = CONST_DISCARD(uint8 *, value);
+       attribs->eas[i].value.length = size;
 
-       status = ea_tdb_save_attrs(rec, attribs);
+       status = xattr_tdb_save_attrs(rec, attribs);
 
        TALLOC_FREE(rec);
 
@@ -307,9 +327,9 @@ static int ea_tdb_setattr(struct db_context *db_ctx,
        return 0;
 }
 
-static int ea_tdb_setxattr(struct vfs_handle_struct *handle,
-                          const char *path, const char *name,
-                          const void *value, size_t size, int flags)
+static int xattr_tdb_setxattr(struct vfs_handle_struct *handle,
+                             const char *path, const char *name,
+                             const void *value, size_t size, int flags)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -317,19 +337,19 @@ static int ea_tdb_setxattr(struct vfs_handle_struct *handle,
 
        SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
 
-       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+       if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       return ea_tdb_setattr(db, &id, name, value, size, flags);
+       return xattr_tdb_setattr(db, &id, name, value, size, flags);
 }
 
-static int ea_tdb_fsetxattr(struct vfs_handle_struct *handle,
-                           struct files_struct *fsp,
-                           const char *name, const void *value,
-                           size_t size, int flags)
+static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
+                              struct files_struct *fsp,
+                              const char *name, const void *value,
+                              size_t size, int flags)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -341,42 +361,43 @@ static int ea_tdb_fsetxattr(struct vfs_handle_struct *handle,
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       return ea_tdb_setattr(db, &id, name, value, size, flags);
+       return xattr_tdb_setattr(db, &id, name, value, size, flags);
 }
 
 /*
  * Worker routine for listxattr and flistxattr
  */
 
-static ssize_t ea_tdb_listattr(struct db_context *db_ctx,
-                              const struct file_id *id, char *list,
-                              size_t size)
+static ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
+                                 const struct file_id *id, char *list,
+                                 size_t size)
 {
        NTSTATUS status;
        struct tdb_xattrs *attribs;
        uint32_t i;
        size_t len = 0;
 
-       status = ea_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
+       status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
+               DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
                           nt_errstr(status)));
                errno = EINVAL;
                return -1;
        }
 
-       DEBUG(10, ("ea_tdb_listattr: Found %d xattrs\n", attribs->num_xattrs));
+       DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
+                  attribs->num_eas));
 
-       for (i=0; i<attribs->num_xattrs; i++) {
+       for (i=0; i<attribs->num_eas; i++) {
                size_t tmp;
 
-               DEBUG(10, ("ea_tdb_listattr: xattrs[i].name: %s\n",
-                          attribs->xattrs[i].name));
+               DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
+                          attribs->eas[i].name));
 
-               tmp = strlen(attribs->xattrs[i].name);
+               tmp = strlen(attribs->eas[i].name);
 
                /*
                 * Try to protect against overflow
@@ -402,18 +423,18 @@ static ssize_t ea_tdb_listattr(struct db_context *db_ctx,
 
        len = 0;
 
-       for (i=0; i<attribs->num_xattrs; i++) {
-               strlcpy(list+len, attribs->xattrs[i].name,
+       for (i=0; i<attribs->num_eas; i++) {
+               strlcpy(list+len, attribs->eas[i].name,
                        size-len);
-               len += (strlen(attribs->xattrs[i].name) + 1);
+               len += (strlen(attribs->eas[i].name) + 1);
        }
 
        TALLOC_FREE(attribs);
        return len;
 }
 
-static ssize_t ea_tdb_listxattr(struct vfs_handle_struct *handle,
-                               const char *path, char *list, size_t size)
+static ssize_t xattr_tdb_listxattr(struct vfs_handle_struct *handle,
+                                  const char *path, char *list, size_t size)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -421,18 +442,18 @@ static ssize_t ea_tdb_listxattr(struct vfs_handle_struct *handle,
 
        SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
 
-       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+       if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       return ea_tdb_listattr(db, &id, list, size);
+       return xattr_tdb_listattr(db, &id, list, size);
 }
 
-static ssize_t ea_tdb_flistxattr(struct vfs_handle_struct *handle,
-                                struct files_struct *fsp, char *list,
-                                size_t size)
+static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
+                                   struct files_struct *fsp, char *list,
+                                   size_t size)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -444,63 +465,63 @@ static ssize_t ea_tdb_flistxattr(struct vfs_handle_struct *handle,
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       return ea_tdb_listattr(db, &id, list, size);
+       return xattr_tdb_listattr(db, &id, list, size);
 }
 
 /*
  * Worker routine for removexattr and fremovexattr
  */
 
-static int ea_tdb_removeattr(struct db_context *db_ctx,
-                            const struct file_id *id, const char *name)
+static int xattr_tdb_removeattr(struct db_context *db_ctx,
+                               const struct file_id *id, const char *name)
 {
        NTSTATUS status;
        struct db_record *rec;
        struct tdb_xattrs *attribs;
        uint32_t i;
 
-       rec = ea_tdb_lock_attrs(talloc_tos(), db_ctx, id);
+       rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
 
        if (rec == NULL) {
-               DEBUG(0, ("ea_tdb_lock_attrs failed\n"));
+               DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
                errno = EINVAL;
                return -1;
        }
 
-       status = ea_tdb_pull_attrs(rec, &rec->value, &attribs);
+       status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("ea_tdb_fetch_attrs failed: %s\n",
+               DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
                           nt_errstr(status)));
                TALLOC_FREE(rec);
                return -1;
        }
 
-       for (i=0; i<attribs->num_xattrs; i++) {
-               if (strcmp(attribs->xattrs[i].name, name) == 0) {
+       for (i=0; i<attribs->num_eas; i++) {
+               if (strcmp(attribs->eas[i].name, name) == 0) {
                        break;
                }
        }
 
-       if (i == attribs->num_xattrs) {
+       if (i == attribs->num_eas) {
                TALLOC_FREE(rec);
                errno = ENOATTR;
                return -1;
        }
 
-       attribs->xattrs[i] =
-               attribs->xattrs[attribs->num_xattrs-1];
-       attribs->num_xattrs -= 1;
+       attribs->eas[i] =
+               attribs->eas[attribs->num_eas-1];
+       attribs->num_eas -= 1;
 
-       if (attribs->num_xattrs == 0) {
+       if (attribs->num_eas == 0) {
                rec->delete_rec(rec);
                TALLOC_FREE(rec);
                return 0;
        }
 
-       status = ea_tdb_save_attrs(rec, attribs);
+       status = xattr_tdb_save_attrs(rec, attribs);
 
        TALLOC_FREE(rec);
 
@@ -512,8 +533,8 @@ static int ea_tdb_removeattr(struct db_context *db_ctx,
        return 0;
 }
 
-static int ea_tdb_removexattr(struct vfs_handle_struct *handle,
-                             const char *path, const char *name)
+static int xattr_tdb_removexattr(struct vfs_handle_struct *handle,
+                                const char *path, const char *name)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -521,17 +542,17 @@ static int ea_tdb_removexattr(struct vfs_handle_struct *handle,
 
        SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
 
-       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+       if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       return ea_tdb_removeattr(db, &id, name);
+       return xattr_tdb_removeattr(db, &id, name);
 }
 
-static int ea_tdb_fremovexattr(struct vfs_handle_struct *handle,
-                              struct files_struct *fsp, const char *name)
+static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
+                                 struct files_struct *fsp, const char *name)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -543,66 +564,99 @@ static int ea_tdb_fremovexattr(struct vfs_handle_struct *handle,
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       return ea_tdb_removeattr(db, &id, name);
+       return xattr_tdb_removeattr(db, &id, name);
 }
 
 /*
  * Open the tdb file upon VFS_CONNECT
  */
 
-static bool ea_tdb_init(int snum, struct db_context **p_db)
+static bool xattr_tdb_init(int snum, struct db_context **p_db)
 {
        struct db_context *db;
        const char *dbname;
+       char *def_dbname;
 
-       dbname = lp_parm_const_string(snum, "ea", "tdb", lock_path("eas.tdb"));
-
-       if (dbname == NULL) {
-               errno = ENOTSUP;
+       def_dbname = state_path("xattr.tdb");
+       if (def_dbname == NULL) {
+               errno = ENOSYS;
                return false;
        }
 
+       dbname = lp_parm_const_string(snum, "xattr_tdb", "file", def_dbname);
+
+       /* now we know dbname is not NULL */
+
        become_root();
        db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
        unbecome_root();
 
        if (db == NULL) {
+#if defined(ENOTSUP)
                errno = ENOTSUP;
+#else
+               errno = ENOSYS;
+#endif
+               TALLOC_FREE(def_dbname);
                return false;
        }
 
        *p_db = db;
+       TALLOC_FREE(def_dbname);
        return true;
 }
 
 /*
  * On unlink we need to delete the tdb record
  */
-static int ea_tdb_unlink(vfs_handle_struct *handle, const char *path)
+static int xattr_tdb_unlink(vfs_handle_struct *handle,
+                           const struct smb_filename *smb_fname)
 {
-       SMB_STRUCT_STAT sbuf;
+       struct smb_filename *smb_fname_tmp = NULL;
        struct file_id id;
        struct db_context *db;
        struct db_record *rec;
-       int ret;
+       NTSTATUS status;
+       int ret = -1;
+       bool remove_record = false;
 
        SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
 
-       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+       status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = map_errno_from_nt_status(status);
                return -1;
        }
 
-       ret = SMB_VFS_NEXT_UNLINK(handle, path);
+       if (lp_posix_pathnames()) {
+               ret = SMB_VFS_LSTAT(handle->conn, smb_fname_tmp);
+       } else {
+               ret = SMB_VFS_STAT(handle->conn, smb_fname_tmp);
+       }
+       if (ret == -1) {
+               goto out;
+       }
+
+       if (smb_fname_tmp->st.st_ex_nlink == 1) {
+               /* Only remove record on last link to file. */
+               remove_record = true;
+       }
+
+       ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_tmp);
 
        if (ret == -1) {
-               return -1;
+               goto out;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       if (!remove_record) {
+               goto out;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &smb_fname_tmp->st);
 
-       rec = ea_tdb_lock_attrs(talloc_tos(), db, &id);
+       rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
 
        /*
         * If rec == NULL there's not much we can do about it
@@ -613,13 +667,15 @@ static int ea_tdb_unlink(vfs_handle_struct *handle, const char *path)
                TALLOC_FREE(rec);
        }
 
-       return 0;
+ out:
+       TALLOC_FREE(smb_fname_tmp);
+       return ret;
 }
 
 /*
  * On rmdir we need to delete the tdb record
  */
-static int ea_tdb_rmdir(vfs_handle_struct *handle, const char *path)
+static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
 {
        SMB_STRUCT_STAT sbuf;
        struct file_id id;
@@ -629,7 +685,7 @@ static int ea_tdb_rmdir(vfs_handle_struct *handle, const char *path)
 
        SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
 
-       if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
+       if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
                return -1;
        }
 
@@ -639,9 +695,9 @@ static int ea_tdb_rmdir(vfs_handle_struct *handle, const char *path)
                return -1;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
 
-       rec = ea_tdb_lock_attrs(talloc_tos(), db, &id);
+       rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
 
        /*
         * If rec == NULL there's not much we can do about it
@@ -659,13 +715,13 @@ static int ea_tdb_rmdir(vfs_handle_struct *handle, const char *path)
  * Destructor for the VFS private data
  */
 
-static void close_ea_db(void **data)
+static void close_xattr_db(void **data)
 {
        struct db_context **p_db = (struct db_context **)data;
        TALLOC_FREE(*p_db);
 }
 
-static int ea_tdb_connect(vfs_handle_struct *handle, const char *service,
+static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
                          const char *user)
 {
        fstring sname;
@@ -677,8 +733,7 @@ static int ea_tdb_connect(vfs_handle_struct *handle, const char *service,
                return res;
        }
 
-       fstrcpy(sname, service);
-       snum = find_service(sname);
+       snum = find_service(service, sname);
        if (snum == -1) {
                /*
                 * Should not happen, but we should not fail just *here*.
@@ -686,51 +741,37 @@ static int ea_tdb_connect(vfs_handle_struct *handle, const char *service,
                return 0;
        }
 
-       if (!ea_tdb_init(snum, &db)) {
-               DEBUG(5, ("Could not init ea tdb\n"));
+       if (!xattr_tdb_init(snum, &db)) {
+               DEBUG(5, ("Could not init xattr tdb\n"));
                lp_do_parameter(snum, "ea support", "False");
                return 0;
        }
 
        lp_do_parameter(snum, "ea support", "True");
 
-       SMB_VFS_HANDLE_SET_DATA(handle, db, close_ea_db,
+       SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
                                struct db_context, return -1);
 
        return 0;
 }
 
-/* VFS operations structure */
-
-static const vfs_op_tuple ea_tdb_ops[] = {
-       {SMB_VFS_OP(ea_tdb_getxattr), SMB_VFS_OP_GETXATTR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_fgetxattr), SMB_VFS_OP_FGETXATTR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_setxattr), SMB_VFS_OP_SETXATTR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_fsetxattr), SMB_VFS_OP_FSETXATTR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_listxattr), SMB_VFS_OP_LISTXATTR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_flistxattr), SMB_VFS_OP_FLISTXATTR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_removexattr), SMB_VFS_OP_REMOVEXATTR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_fremovexattr), SMB_VFS_OP_FREMOVEXATTR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_unlink), SMB_VFS_OP_UNLINK,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_rmdir), SMB_VFS_OP_RMDIR,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(ea_tdb_connect), SMB_VFS_OP_CONNECT,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
+       .getxattr = xattr_tdb_getxattr,
+       .fgetxattr = xattr_tdb_fgetxattr,
+       .setxattr = xattr_tdb_setxattr,
+       .fsetxattr = xattr_tdb_fsetxattr,
+       .listxattr = xattr_tdb_listxattr,
+       .flistxattr = xattr_tdb_flistxattr,
+       .removexattr = xattr_tdb_removexattr,
+       .fremovexattr = xattr_tdb_fremovexattr,
+       .unlink = xattr_tdb_unlink,
+       .rmdir = xattr_tdb_rmdir,
+       .connect_fn = xattr_tdb_connect,
 };
 
-NTSTATUS vfs_ea_tdb_init(void);
-NTSTATUS vfs_ea_tdb_init(void)
+NTSTATUS vfs_xattr_tdb_init(void);
+NTSTATUS vfs_xattr_tdb_init(void)
 {
-       return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ea_tdb",
-                               ea_tdb_ops);
+       return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",
+                               &vfs_xattr_tdb_fns);
 }