vfs: change openat propotype to match linux openat2
[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                             const struct vfs_open_how *how)
369 {
370         struct db_context *db = NULL;
371         TALLOC_CTX *frame = NULL;
372         SMB_STRUCT_STAT sbuf;
373         int fd;
374         int ret;
375
376         fd = SMB_VFS_NEXT_OPENAT(handle,
377                                  dirfsp,
378                                  smb_fname,
379                                  fsp,
380                                  how);
381         if (fd == -1) {
382                 return -1;
383         }
384
385         if ((how->flags & (O_CREAT|O_EXCL)) != (O_CREAT|O_EXCL)) {
386                 return fd;
387         }
388
389         /*
390          * We know we used O_CREAT|O_EXCL and it worked.
391          * We must have created the file.
392          */
393
394         fsp_set_fd(fsp, fd);
395         ret = SMB_VFS_FSTAT(fsp, &sbuf);
396         fsp_set_fd(fsp, -1);
397         if (ret == -1) {
398                 /* Can't happen... */
399                 DBG_WARNING("SMB_VFS_FSTAT failed on file %s (%s)\n",
400                             smb_fname_str_dbg(smb_fname),
401                             strerror(errno));
402                 return -1;
403         }
404
405         fsp->file_id = SMB_VFS_FILE_ID_CREATE(fsp->conn, &sbuf);
406
407         frame = talloc_stackframe();
408
409         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
410                                 if (!xattr_tdb_init(-1, frame, &db))
411                                 {
412                                         TALLOC_FREE(frame); return -1;
413                                 });
414
415         xattr_tdb_remove_all_attrs(db, &fsp->file_id);
416
417         TALLOC_FREE(frame);
418         return fd;
419 }
420
421 static int xattr_tdb_mkdirat(vfs_handle_struct *handle,
422                 struct files_struct *dirfsp,
423                 const struct smb_filename *smb_fname,
424                 mode_t mode)
425 {
426         struct db_context *db = NULL;
427         TALLOC_CTX *frame = NULL;
428         struct file_id fileid;
429         int ret;
430         struct smb_filename *full_fname = NULL;
431
432         ret = SMB_VFS_NEXT_MKDIRAT(handle,
433                                 dirfsp,
434                                 smb_fname,
435                                 mode);
436         if (ret < 0) {
437                 return ret;
438         }
439
440         frame = talloc_stackframe();
441
442         full_fname = full_path_from_dirfsp_atname(talloc_tos(),
443                                                   dirfsp,
444                                                   smb_fname);
445         if (full_fname == NULL) {
446                 errno = ENOMEM;
447                 return -1;
448         }
449
450         /* Always use LSTAT here - we just created the directory. */
451         ret = SMB_VFS_LSTAT(handle->conn, full_fname);
452         if (ret == -1) {
453                 /* Rename race. Let upper level take care of it. */
454                 TALLOC_FREE(frame);
455                 return -1;
456         }
457         if (!S_ISDIR(full_fname->st.st_ex_mode)) {
458                 /* Rename race. Let upper level take care of it. */
459                 TALLOC_FREE(frame);
460                 return -1;
461         }
462
463         fileid = SMB_VFS_FILE_ID_CREATE(handle->conn, &full_fname->st);
464
465         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
466                                 if (!xattr_tdb_init(-1, frame, &db))
467                                 {
468                                         TALLOC_FREE(frame); return -1;
469                                 });
470
471         xattr_tdb_remove_all_attrs(db, &fileid);
472         TALLOC_FREE(frame);
473         return 0;
474 }
475
476 /*
477  * On unlink we need to delete the tdb record
478  */
479 static int xattr_tdb_unlinkat(vfs_handle_struct *handle,
480                         struct files_struct *dirfsp,
481                         const struct smb_filename *smb_fname,
482                         int flags)
483 {
484         struct smb_filename *smb_fname_tmp = NULL;
485         struct smb_filename *full_fname = NULL;
486         struct file_id id;
487         struct db_context *db;
488         int ret = -1;
489         bool remove_record = false;
490         TALLOC_CTX *frame = talloc_stackframe();
491
492         SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
493                                 if (!xattr_tdb_init(-1, frame, &db))
494                                 {
495                                         TALLOC_FREE(frame); return -1;
496                                 });
497
498         smb_fname_tmp = cp_smb_filename(frame, smb_fname);
499         if (smb_fname_tmp == NULL) {
500                 TALLOC_FREE(frame);
501                 errno = ENOMEM;
502                 return -1;
503         }
504
505         /*
506          * TODO: use SMB_VFS_STATX() once we have that
507          */
508
509         full_fname = full_path_from_dirfsp_atname(frame,
510                                                   dirfsp,
511                                                   smb_fname);
512         if (full_fname == NULL) {
513                 goto out;
514         }
515
516         if (full_fname->flags & SMB_FILENAME_POSIX_PATH) {
517                 ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
518         } else {
519                 ret = SMB_VFS_NEXT_STAT(handle, full_fname);
520                 if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
521                         if (VALID_STAT(smb_fname->st) &&
522                                         S_ISLNK(smb_fname->st.st_ex_mode)) {
523                                 /*
524                                  * Original name was a link - Could be
525                                  * trying to remove a dangling symlink.
526                                  */
527                                 ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
528                         }
529                 }
530         }
531         if (ret == -1) {
532                 goto out;
533         }
534         smb_fname_tmp->st = full_fname->st;
535
536         if (flags & AT_REMOVEDIR) {
537                 /* Always remove record when removing a directory succeeds. */
538                 remove_record = true;
539         } else {
540                 if (smb_fname_tmp->st.st_ex_nlink == 1) {
541                         /* Only remove record on last link to file. */
542                         remove_record = true;
543                 }
544         }
545
546         ret = SMB_VFS_NEXT_UNLINKAT(handle,
547                                 dirfsp,
548                                 smb_fname_tmp,
549                                 flags);
550
551         if (ret == -1) {
552                 goto out;
553         }
554
555         if (!remove_record) {
556                 goto out;
557         }
558
559         id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &smb_fname_tmp->st);
560
561         xattr_tdb_remove_all_attrs(db, &id);
562
563  out:
564         TALLOC_FREE(frame);
565         return ret;
566 }
567
568 /*
569  * Destructor for the VFS private data
570  */
571
572 static void close_xattr_db(void **data)
573 {
574         struct db_context **p_db = (struct db_context **)data;
575         TALLOC_FREE(*p_db);
576 }
577
578 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
579                           const char *user)
580 {
581         char *sname = NULL;
582         int res, snum;
583         struct db_context *db;
584
585         res = SMB_VFS_NEXT_CONNECT(handle, service, user);
586         if (res < 0) {
587                 return res;
588         }
589
590         snum = find_service(talloc_tos(), service, &sname);
591         if (snum == -1 || sname == NULL) {
592                 /*
593                  * Should not happen, but we should not fail just *here*.
594                  */
595                 return 0;
596         }
597
598         if (!xattr_tdb_init(snum, NULL, &db)) {
599                 DEBUG(5, ("Could not init xattr tdb\n"));
600                 lp_do_parameter(snum, "ea support", "False");
601                 return 0;
602         }
603
604         lp_do_parameter(snum, "ea support", "True");
605
606         SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
607                                 struct db_context, return -1);
608
609         return 0;
610 }
611
612 static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
613         .getxattrat_send_fn = xattr_tdb_getxattrat_send,
614         .getxattrat_recv_fn = xattr_tdb_getxattrat_recv,
615         .fgetxattr_fn = xattr_tdb_fgetxattr,
616         .fsetxattr_fn = xattr_tdb_fsetxattr,
617         .flistxattr_fn = xattr_tdb_flistxattr,
618         .fremovexattr_fn = xattr_tdb_fremovexattr,
619         .openat_fn = xattr_tdb_openat,
620         .mkdirat_fn = xattr_tdb_mkdirat,
621         .unlinkat_fn = xattr_tdb_unlinkat,
622         .connect_fn = xattr_tdb_connect,
623 };
624
625 static_decl_vfs;
626 NTSTATUS vfs_xattr_tdb_init(TALLOC_CTX *ctx)
627 {
628         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",
629                                 &vfs_xattr_tdb_fns);
630 }