s3: VFS: xattr_tdb. Allow unlinkat to cope with dangling symlinks.
[samba.git] / source3 / modules / vfs_xattr_tdb.c
1 /*
2  * Store posix-level xattrs in a tdb
3  *
4  * Copyright (C) Volker Lendecke, 2007
5  * Copyright (C) Andrew Bartlett, 2012
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "dbwrap/dbwrap.h"
25 #include "dbwrap/dbwrap_open.h"
26 #include "source3/lib/xattr_tdb.h"
27 #include "lib/util/tevent_unix.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_VFS
31
32 static bool xattr_tdb_init(int snum, TALLOC_CTX *mem_ctx, struct db_context **p_db);
33
34 static int xattr_tdb_get_file_id(struct vfs_handle_struct *handle,
35                                 const char *path, struct file_id *id)
36 {
37         int ret;
38         TALLOC_CTX *frame = talloc_stackframe();
39         struct smb_filename *smb_fname;
40
41         smb_fname = synthetic_smb_fname(frame,
42                                         path,
43                                         NULL,
44                                         NULL,
45                                         0,
46                                         0);
47         if (smb_fname == NULL) {
48                 TALLOC_FREE(frame);
49                 errno = ENOMEM;
50                 return -1;
51         }
52
53         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
54
55         if (ret == -1) {
56                 TALLOC_FREE(frame); 
57                 return -1;
58         }
59
60         *id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &smb_fname->st);
61         TALLOC_FREE(frame);
62         return 0;
63 }
64
65 struct xattr_tdb_getxattrat_state {
66         struct vfs_aio_state vfs_aio_state;
67         ssize_t xattr_size;
68         uint8_t *xattr_value;
69 };
70
71 static struct tevent_req *xattr_tdb_getxattrat_send(
72                         TALLOC_CTX *mem_ctx,
73                         struct tevent_context *ev,
74                         struct vfs_handle_struct *handle,
75                         files_struct *dir_fsp,
76                         const struct smb_filename *smb_fname,
77                         const char *xattr_name,
78                         size_t alloc_hint)
79 {
80         struct tevent_req *req = NULL;
81         struct xattr_tdb_getxattrat_state *state = NULL;
82         struct smb_filename *cwd = NULL;
83         struct db_context *db = NULL;
84         struct file_id id;
85         int ret;
86         int error;
87         int cwd_ret;
88         DATA_BLOB xattr_blob;
89
90         req = tevent_req_create(mem_ctx, &state,
91                                 struct xattr_tdb_getxattrat_state);
92         if (req == NULL) {
93                 return NULL;
94         }
95         state->xattr_size = -1;
96
97         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
98                                 if (!xattr_tdb_init(-1, state, &db)) {
99                                         tevent_req_error(req, EIO);
100                                         return tevent_req_post(req, ev);
101                                 });
102
103         cwd = SMB_VFS_GETWD(dir_fsp->conn, state);
104         if (tevent_req_nomem(cwd, req)) {
105                 return tevent_req_post(req, ev);
106         }
107
108         ret = SMB_VFS_CHDIR(dir_fsp->conn, dir_fsp->fsp_name);
109         if (ret != 0) {
110                 tevent_req_error(req, errno);
111                 return tevent_req_post(req, ev);
112         }
113
114         ret = xattr_tdb_get_file_id(handle, smb_fname->base_name, &id);
115         error = errno;
116
117         cwd_ret = SMB_VFS_CHDIR(dir_fsp->conn, cwd);
118         SMB_ASSERT(cwd_ret == 0);
119
120         if (ret == -1) {
121                 tevent_req_error(req, error);
122                 return tevent_req_post(req, ev);
123         }
124
125         state->xattr_size = xattr_tdb_getattr(db,
126                                               state,
127                                               &id,
128                                               xattr_name,
129                                               &xattr_blob);
130         if (state->xattr_size == -1) {
131                 tevent_req_error(req, errno);
132                 return tevent_req_post(req, ev);
133         }
134
135         if (alloc_hint == 0) {
136                 /*
137                  * The caller only wants to know the size.
138                  */
139                 tevent_req_done(req);
140                 return tevent_req_post(req, ev);
141         }
142
143         if (state->xattr_size == 0) {
144                 /*
145                  * There's no data.
146                  */
147                 tevent_req_done(req);
148                 return tevent_req_post(req, ev);
149         }
150
151         if (xattr_blob.length > alloc_hint) {
152                 /*
153                  * The data doesn't fit.
154                  */
155                 state->xattr_size = -1;
156                 tevent_req_error(req, ERANGE);
157                 return tevent_req_post(req, ev);
158         }
159
160         /*
161          * take the whole blob.
162          */
163         state->xattr_value = xattr_blob.data;
164
165         tevent_req_done(req);
166         return tevent_req_post(req, ev);
167 }
168
169 static ssize_t xattr_tdb_getxattrat_recv(struct tevent_req *req,
170                                          struct vfs_aio_state *aio_state,
171                                          TALLOC_CTX *mem_ctx,
172                                          uint8_t **xattr_value)
173 {
174         struct xattr_tdb_getxattrat_state *state = tevent_req_data(
175                 req, struct xattr_tdb_getxattrat_state);
176         ssize_t xattr_size;
177
178         if (tevent_req_is_unix_error(req, &aio_state->error)) {
179                 tevent_req_received(req);
180                 return -1;
181         }
182
183         *aio_state = state->vfs_aio_state;
184         xattr_size = state->xattr_size;
185         if (xattr_value != NULL) {
186                 *xattr_value = talloc_move(mem_ctx, &state->xattr_value);
187         }
188
189         tevent_req_received(req);
190         return xattr_size;
191 }
192
193 static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
194                                    struct files_struct *fsp,
195                                    const char *name, void *value, size_t size)
196 {
197         SMB_STRUCT_STAT sbuf;
198         struct file_id id;
199         struct db_context *db;
200         ssize_t xattr_size;
201         DATA_BLOB blob;
202         TALLOC_CTX *frame = talloc_stackframe();
203
204         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
205                                 if (!xattr_tdb_init(-1, frame, &db))
206                                 {
207                                         TALLOC_FREE(frame); return -1;
208                                 });
209
210         if (SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf) == -1) {
211                 TALLOC_FREE(frame);
212                 return -1;
213         }
214
215         id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sbuf);
216
217         xattr_size = xattr_tdb_getattr(db, frame, &id, name, &blob);
218         if (xattr_size < 0) {
219                 errno = ENOATTR;
220                 TALLOC_FREE(frame);
221                 return -1;
222         }
223
224         if (size == 0) {
225                 TALLOC_FREE(frame);
226                 return xattr_size;
227         }
228
229         if (blob.length > size) {
230                 TALLOC_FREE(frame);
231                 errno = ERANGE;
232                 return -1;
233         }
234         memcpy(value, blob.data, xattr_size);
235         TALLOC_FREE(frame);
236         return xattr_size;
237 }
238
239 static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
240                                struct files_struct *fsp,
241                                const char *name, const void *value,
242                                size_t size, int flags)
243 {
244         SMB_STRUCT_STAT sbuf;
245         struct file_id id;
246         struct db_context *db;
247         int ret;
248         TALLOC_CTX *frame = talloc_stackframe();
249
250         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
251                                 if (!xattr_tdb_init(-1, frame, &db))
252                                 {
253                                         TALLOC_FREE(frame); return -1;
254                                 });
255
256         if (SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf) == -1) {
257                 TALLOC_FREE(frame);
258                 return -1;
259         }
260
261         id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sbuf);
262
263         ret = xattr_tdb_setattr(db, &id, name, value, size, flags);
264         TALLOC_FREE(frame);
265         return ret;
266
267 }
268
269 static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
270                                     struct files_struct *fsp, char *list,
271                                     size_t size)
272 {
273         SMB_STRUCT_STAT sbuf;
274         struct file_id id;
275         struct db_context *db;
276         int ret;
277         TALLOC_CTX *frame = talloc_stackframe();
278
279         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
280                                 if (!xattr_tdb_init(-1, frame, &db))
281                                 {
282                                         TALLOC_FREE(frame); return -1;
283                                 });
284
285         if (SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf) == -1) {
286                 TALLOC_FREE(frame);
287                 return -1;
288         }
289
290         id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sbuf);
291
292         ret = xattr_tdb_listattr(db, &id, list, size);
293         TALLOC_FREE(frame);
294         return ret;
295 }
296
297 static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
298                                   struct files_struct *fsp, const char *name)
299 {
300         SMB_STRUCT_STAT sbuf;
301         struct file_id id;
302         struct db_context *db;
303         int ret;
304         TALLOC_CTX *frame = talloc_stackframe();
305
306         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
307                                 if (!xattr_tdb_init(-1, frame, &db))
308                                 {
309                                         TALLOC_FREE(frame); return -1;
310                                 });
311
312         if (SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf) == -1) {
313                 TALLOC_FREE(frame);
314                 return -1;
315         }
316
317         id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sbuf);
318
319         ret = xattr_tdb_removeattr(db, &id, name);
320         TALLOC_FREE(frame);
321         return ret;
322 }
323
324 /*
325  * Open the tdb file upon VFS_CONNECT
326  */
327
328 static bool xattr_tdb_init(int snum, TALLOC_CTX *mem_ctx, struct db_context **p_db)
329 {
330         struct db_context *db;
331         const char *dbname;
332         char *def_dbname;
333
334         def_dbname = state_path(talloc_tos(), "xattr.tdb");
335         if (def_dbname == NULL) {
336                 errno = ENOSYS;
337                 return false;
338         }
339
340         dbname = lp_parm_const_string(snum, "xattr_tdb", "file", def_dbname);
341
342         /* now we know dbname is not NULL */
343
344         become_root();
345         db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600,
346                      DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE);
347         unbecome_root();
348
349         if (db == NULL) {
350 #if defined(ENOTSUP)
351                 errno = ENOTSUP;
352 #else
353                 errno = ENOSYS;
354 #endif
355                 TALLOC_FREE(def_dbname);
356                 return false;
357         }
358
359         *p_db = db;
360         TALLOC_FREE(def_dbname);
361         return true;
362 }
363
364 static int xattr_tdb_openat(struct vfs_handle_struct *handle,
365                             const struct files_struct *dirfsp,
366                             const struct smb_filename *smb_fname,
367                             struct files_struct *fsp,
368                             int flags,
369                             mode_t mode)
370 {
371         struct db_context *db = NULL;
372         TALLOC_CTX *frame = NULL;
373         SMB_STRUCT_STAT sbuf;
374         int fd;
375         int ret;
376
377         fd = SMB_VFS_NEXT_OPENAT(handle,
378                                  dirfsp,
379                                  smb_fname,
380                                  fsp,
381                                  flags,
382                                  mode);
383
384         if (fd == -1) {
385                 return -1;
386         }
387
388         if ((flags & (O_CREAT|O_EXCL)) != (O_CREAT|O_EXCL)) {
389                 return fd;
390         }
391
392         /*
393          * We know we used O_CREAT|O_EXCL and it worked.
394          * We must have created the file.
395          */
396
397         fsp_set_fd(fsp, fd);
398         ret = SMB_VFS_FSTAT(fsp, &sbuf);
399         fsp_set_fd(fsp, -1);
400         if (ret == -1) {
401                 /* Can't happen... */
402                 DBG_WARNING("SMB_VFS_FSTAT failed on file %s (%s)\n",
403                             smb_fname_str_dbg(smb_fname),
404                             strerror(errno));
405                 return -1;
406         }
407
408         fsp->file_id = SMB_VFS_FILE_ID_CREATE(fsp->conn, &sbuf);
409
410         frame = talloc_stackframe();
411
412         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
413                                 if (!xattr_tdb_init(-1, frame, &db))
414                                 {
415                                         TALLOC_FREE(frame); return -1;
416                                 });
417
418         xattr_tdb_remove_all_attrs(db, &fsp->file_id);
419
420         TALLOC_FREE(frame);
421         return fd;
422 }
423
424 static int xattr_tdb_mkdirat(vfs_handle_struct *handle,
425                 struct files_struct *dirfsp,
426                 const struct smb_filename *smb_fname,
427                 mode_t mode)
428 {
429         struct db_context *db = NULL;
430         TALLOC_CTX *frame = NULL;
431         struct file_id fileid;
432         int ret;
433         struct smb_filename *full_fname = NULL;
434
435         ret = SMB_VFS_NEXT_MKDIRAT(handle,
436                                 dirfsp,
437                                 smb_fname,
438                                 mode);
439         if (ret < 0) {
440                 return ret;
441         }
442
443         frame = talloc_stackframe();
444
445         full_fname = full_path_from_dirfsp_atname(talloc_tos(),
446                                                   dirfsp,
447                                                   smb_fname);
448         if (full_fname == NULL) {
449                 errno = ENOMEM;
450                 return -1;
451         }
452
453         /* Always use LSTAT here - we just created the directory. */
454         ret = SMB_VFS_LSTAT(handle->conn, full_fname);
455         if (ret == -1) {
456                 /* Rename race. Let upper level take care of it. */
457                 TALLOC_FREE(frame);
458                 return -1;
459         }
460         if (!S_ISDIR(full_fname->st.st_ex_mode)) {
461                 /* Rename race. Let upper level take care of it. */
462                 TALLOC_FREE(frame);
463                 return -1;
464         }
465
466         fileid = SMB_VFS_FILE_ID_CREATE(handle->conn, &full_fname->st);
467
468         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
469                                 if (!xattr_tdb_init(-1, frame, &db))
470                                 {
471                                         TALLOC_FREE(frame); return -1;
472                                 });
473
474         xattr_tdb_remove_all_attrs(db, &fileid);
475         TALLOC_FREE(frame);
476         return 0;
477 }
478
479 /*
480  * On unlink we need to delete the tdb record
481  */
482 static int xattr_tdb_unlinkat(vfs_handle_struct *handle,
483                         struct files_struct *dirfsp,
484                         const struct smb_filename *smb_fname,
485                         int flags)
486 {
487         struct smb_filename *smb_fname_tmp = NULL;
488         struct smb_filename *full_fname = NULL;
489         struct file_id id;
490         struct db_context *db;
491         int ret = -1;
492         bool remove_record = false;
493         TALLOC_CTX *frame = talloc_stackframe();
494
495         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
496                                 if (!xattr_tdb_init(-1, frame, &db))
497                                 {
498                                         TALLOC_FREE(frame); return -1;
499                                 });
500
501         smb_fname_tmp = cp_smb_filename(frame, smb_fname);
502         if (smb_fname_tmp == NULL) {
503                 TALLOC_FREE(frame);
504                 errno = ENOMEM;
505                 return -1;
506         }
507
508         /*
509          * TODO: use SMB_VFS_STATX() once we have that
510          */
511
512         full_fname = full_path_from_dirfsp_atname(frame,
513                                                   dirfsp,
514                                                   smb_fname);
515         if (full_fname == NULL) {
516                 goto out;
517         }
518
519         if (full_fname->flags & SMB_FILENAME_POSIX_PATH) {
520                 ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
521         } else {
522                 ret = SMB_VFS_NEXT_STAT(handle, full_fname);
523                 if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
524                         if (VALID_STAT(smb_fname->st) &&
525                                         S_ISLNK(smb_fname->st.st_ex_mode)) {
526                                 /*
527                                  * Original name was a link - Could be
528                                  * trying to remove a dangling symlink.
529                                  */
530                                 ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
531                         }
532                 }
533         }
534         if (ret == -1) {
535                 goto out;
536         }
537         smb_fname_tmp->st = full_fname->st;
538
539         if (flags & AT_REMOVEDIR) {
540                 /* Always remove record when removing a directory succeeds. */
541                 remove_record = true;
542         } else {
543                 if (smb_fname_tmp->st.st_ex_nlink == 1) {
544                         /* Only remove record on last link to file. */
545                         remove_record = true;
546                 }
547         }
548
549         ret = SMB_VFS_NEXT_UNLINKAT(handle,
550                                 dirfsp,
551                                 smb_fname_tmp,
552                                 flags);
553
554         if (ret == -1) {
555                 goto out;
556         }
557
558         if (!remove_record) {
559                 goto out;
560         }
561
562         id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &smb_fname_tmp->st);
563
564         xattr_tdb_remove_all_attrs(db, &id);
565
566  out:
567         TALLOC_FREE(frame);
568         return ret;
569 }
570
571 /*
572  * Destructor for the VFS private data
573  */
574
575 static void close_xattr_db(void **data)
576 {
577         struct db_context **p_db = (struct db_context **)data;
578         TALLOC_FREE(*p_db);
579 }
580
581 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
582                           const char *user)
583 {
584         char *sname = NULL;
585         int res, snum;
586         struct db_context *db;
587
588         res = SMB_VFS_NEXT_CONNECT(handle, service, user);
589         if (res < 0) {
590                 return res;
591         }
592
593         snum = find_service(talloc_tos(), service, &sname);
594         if (snum == -1 || sname == NULL) {
595                 /*
596                  * Should not happen, but we should not fail just *here*.
597                  */
598                 return 0;
599         }
600
601         if (!xattr_tdb_init(snum, NULL, &db)) {
602                 DEBUG(5, ("Could not init xattr tdb\n"));
603                 lp_do_parameter(snum, "ea support", "False");
604                 return 0;
605         }
606
607         lp_do_parameter(snum, "ea support", "True");
608
609         SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
610                                 struct db_context, return -1);
611
612         return 0;
613 }
614
615 static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
616         .getxattrat_send_fn = xattr_tdb_getxattrat_send,
617         .getxattrat_recv_fn = xattr_tdb_getxattrat_recv,
618         .fgetxattr_fn = xattr_tdb_fgetxattr,
619         .fsetxattr_fn = xattr_tdb_fsetxattr,
620         .flistxattr_fn = xattr_tdb_flistxattr,
621         .fremovexattr_fn = xattr_tdb_fremovexattr,
622         .openat_fn = xattr_tdb_openat,
623         .mkdirat_fn = xattr_tdb_mkdirat,
624         .unlinkat_fn = xattr_tdb_unlinkat,
625         .connect_fn = xattr_tdb_connect,
626 };
627
628 static_decl_vfs;
629 NTSTATUS vfs_xattr_tdb_init(TALLOC_CTX *ctx)
630 {
631         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",
632                                 &vfs_xattr_tdb_fns);
633 }