Merge branch 'master' of git://git.samba.org/samba into minschema
[samba.git] / source3 / modules / onefs_streams.c
1 /*
2  * Unix SMB/CIFS implementation.
3  *
4  * Support for OneFS Alternate Data Streams
5  *
6  * Copyright (C) Tim Prouty, 2008
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "includes.h"
23 #include "onefs.h"
24 #include "onefs_config.h"
25
26 #include <sys/isi_enc.h>
27
28 /*
29  * OneFS stores streams without the explicit :$DATA at the end, so this strips
30  * it off.  All onefs_stream functions must call through this instead of
31  * split_ntfs_stream_name directly.
32  */
33 NTSTATUS onefs_split_ntfs_stream_name(TALLOC_CTX *mem_ctx, const char *fname,
34                                       char **pbase, char **pstream)
35 {
36         NTSTATUS status;
37         char *stream;
38
39         status = split_ntfs_stream_name(mem_ctx, fname, pbase, pstream);
40         if (!NT_STATUS_IS_OK(status)) {
41                 return status;
42         }
43
44         /* Default $DATA stream.  */
45         if (pstream == NULL || *pstream == NULL) {
46                 return NT_STATUS_OK;
47         }
48
49         /* Strip off the $DATA. */
50         stream = strrchr_m(*pstream, ':');
51         SMB_ASSERT(stream);
52         stream[0] = '\0';
53
54         return NT_STATUS_OK;
55 }
56
57 int onefs_is_stream(const char *path, char **pbase, char **pstream,
58                     bool *is_stream)
59 {
60         (*is_stream) = is_ntfs_stream_name(path);
61
62         if (!(*is_stream)) {
63                 return 0;
64         }
65
66         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
67                                                           pbase, pstream))) {
68                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
69                 errno = ENOMEM;
70                 return -1;
71         }
72
73         return 0;
74 }
75
76 int onefs_close(vfs_handle_struct *handle, struct files_struct *fsp)
77 {
78         int ret2, ret = 0;
79
80         if (fsp->base_fsp) {
81                 ret = SMB_VFS_NEXT_CLOSE(handle, fsp->base_fsp);
82         }
83         ret2 = SMB_VFS_NEXT_CLOSE(handle, fsp);
84
85         return ret ? ret : ret2;
86 }
87
88 /*
89  * Get the ADS directory fd for a file.
90  */
91 static int get_stream_dir_fd(connection_struct *conn, const char *base,
92                              int *base_fdp)
93 {
94         int base_fd;
95         int dir_fd;
96         int saved_errno;
97
98         /* If a valid base_fdp was given, use it. */
99         if (base_fdp && *base_fdp >= 0) {
100                 base_fd = *base_fdp;
101         } else {
102                 base_fd = onefs_sys_create_file(conn,
103                                                 -1,
104                                                 base,
105                                                 0,
106                                                 0,
107                                                 0,
108                                                 0,
109                                                 0,
110                                                 0,
111                                                 INTERNAL_OPEN_ONLY,
112                                                 0,
113                                                 NULL,
114                                                 0,
115                                                 NULL);
116                 if (base_fd < 0) {
117                         return -1;
118                 }
119         }
120
121         /* Open the ADS directory. */
122         dir_fd = onefs_sys_create_file(conn,
123                                         base_fd,
124                                         ".",
125                                         0,
126                                         FILE_READ_DATA,
127                                         0,
128                                         0,
129                                         0,
130                                         0,
131                                         INTERNAL_OPEN_ONLY,
132                                         0,
133                                         NULL,
134                                         0,
135                                         NULL);
136
137         /* Close base_fd if it's not need or on error. */
138         if (!base_fdp || dir_fd < 0) {
139                 saved_errno = errno;
140                 close(base_fd);
141                 errno = saved_errno;
142         }
143
144         /* Set the out base_fdp if successful and it was requested. */
145         if (base_fdp && dir_fd >= 0) {
146                 *base_fdp = base_fd;
147         }
148
149         return dir_fd;
150 }
151
152 int onefs_rename(vfs_handle_struct *handle, const char *oldname,
153                  const char *newname)
154 {
155         TALLOC_CTX *frame = NULL;
156         int ret = -1;
157         int dir_fd = -1;
158         int saved_errno;
159         bool old_is_stream;
160         bool new_is_stream;
161         char *obase = NULL;
162         char *osname = NULL;
163         char *nbase = NULL;
164         char *nsname = NULL;
165
166         START_PROFILE(syscall_rename_at);
167
168         frame = talloc_stackframe();
169
170         ret = onefs_is_stream(oldname, &obase, &osname, &old_is_stream);
171         if (ret) {
172                 END_PROFILE(syscall_rename_at);
173                 return ret;
174         }
175
176         ret = onefs_is_stream(newname, &nbase, &nsname, &new_is_stream);
177         if (ret) {
178                 END_PROFILE(syscall_rename_at);
179                 return ret;
180         }
181
182         if (!old_is_stream && !new_is_stream) {
183                 ret = SMB_VFS_NEXT_RENAME(handle, oldname, newname);
184                 END_PROFILE(syscall_rename_at);
185                 return ret;
186         }
187
188         dir_fd = get_stream_dir_fd(handle->conn, obase, NULL);
189         if (dir_fd < -1) {
190                 goto done;
191         }
192
193         DEBUG(8,("onefs_rename called for %s : %s  => %s : %s\n",
194                 obase, osname,  nbase, nsname));
195
196         /* Handle rename of stream to default stream specially. */
197         if (nsname == NULL) {
198                 ret = enc_renameat(dir_fd, osname, ENC_DEFAULT, AT_FDCWD,
199                                    nbase, ENC_DEFAULT);
200         } else {
201                 ret = enc_renameat(dir_fd, osname, ENC_DEFAULT, dir_fd, nsname,
202                                    ENC_DEFAULT);
203         }
204
205  done:
206         END_PROFILE(syscall_rename_at);
207
208         saved_errno = errno;
209         if (dir_fd >= 0) {
210                 close(dir_fd);
211         }
212         errno = saved_errno;
213         TALLOC_FREE(frame);
214         return ret;
215 }
216
217 /*
218  * Merge a base file's sbuf into the a streams's sbuf.
219  */
220 static void merge_stat(SMB_STRUCT_STAT *stream_sbuf,
221                        const SMB_STRUCT_STAT *base_sbuf)
222 {
223         int dos_flags = (UF_DOS_NOINDEX | UF_DOS_ARCHIVE |
224             UF_DOS_HIDDEN | UF_DOS_RO | UF_DOS_SYSTEM);
225         stream_sbuf->st_mtime = base_sbuf->st_mtime;
226         stream_sbuf->st_ctime = base_sbuf->st_ctime;
227         stream_sbuf->st_atime = base_sbuf->st_atime;
228         stream_sbuf->st_flags &= ~dos_flags;
229         stream_sbuf->st_flags |= base_sbuf->st_flags & dos_flags;
230 }
231
232 /* fake timestamps */
233 static void onefs_adjust_stat_time(vfs_handle_struct *handle, const char *fname,
234                                    SMB_STRUCT_STAT *sbuf)
235 {
236         struct onefs_vfs_share_config cfg;
237         struct timeval tv_now = {0, 0};
238         bool static_mtime = False;
239         bool static_atime = False;
240
241         if (!onefs_get_config(SNUM(handle->conn),
242                               ONEFS_VFS_CONFIG_FAKETIMESTAMPS, &cfg)) {
243                 return;
244         }
245
246         if (IS_MTIME_STATIC_PATH(handle->conn, &cfg, fname)) {
247                 sbuf->st_mtime = sbuf->st_birthtime;
248                 static_mtime = True;
249         }
250         if (IS_ATIME_STATIC_PATH(handle->conn, &cfg, fname)) {
251                 sbuf->st_atime = sbuf->st_birthtime;
252                 static_atime = True;
253         }
254
255         if (IS_CTIME_NOW_PATH(handle->conn, &cfg, fname)) {
256                 if (cfg.ctime_slop < 0) {
257                         sbuf->st_birthtime = INT_MAX - 1;
258                 } else {
259                         GetTimeOfDay(&tv_now);
260                         sbuf->st_birthtime = tv_now.tv_sec + cfg.ctime_slop;
261                 }
262         }
263
264         if (!static_mtime && IS_MTIME_NOW_PATH(handle->conn,&cfg,fname)) {
265                 if (cfg.mtime_slop < 0) {
266                         sbuf->st_mtime = INT_MAX - 1;
267                 } else {
268                         if (tv_now.tv_sec == 0)
269                                 GetTimeOfDay(&tv_now);
270                         sbuf->st_mtime = tv_now.tv_sec + cfg.mtime_slop;
271                 }
272         }
273         if (!static_atime && IS_ATIME_NOW_PATH(handle->conn,&cfg,fname)) {
274                 if (cfg.atime_slop < 0) {
275                         sbuf->st_atime = INT_MAX - 1;
276                 } else {
277                         if (tv_now.tv_sec == 0)
278                                 GetTimeOfDay(&tv_now);
279                         sbuf->st_atime = tv_now.tv_sec + cfg.atime_slop;
280                 }
281         }
282 }
283
284 static int stat_stream(vfs_handle_struct *handle, const char *base,
285                        const char *stream, SMB_STRUCT_STAT *sbuf, int flags)
286 {
287         SMB_STRUCT_STAT base_sbuf;
288         int base_fd = -1, dir_fd, ret, saved_errno;
289
290         dir_fd = get_stream_dir_fd(handle->conn, base, &base_fd);
291         if (dir_fd < 0) {
292                 return -1;
293         }
294
295         /* Stat the stream. */
296         ret = enc_fstatat(dir_fd, stream, ENC_DEFAULT, sbuf, flags);
297         if (ret != -1) {
298                 /* Now stat the base file and merge the results. */
299                 ret = sys_fstat(base_fd, &base_sbuf);
300                 if (ret != -1) {
301                         merge_stat(sbuf, &base_sbuf);
302                 }
303         }
304
305         saved_errno = errno;
306         close(dir_fd);
307         close(base_fd);
308         errno = saved_errno;
309         return ret;
310 }
311
312 int onefs_stat(vfs_handle_struct *handle, const char *path,
313                SMB_STRUCT_STAT *sbuf)
314 {
315         int ret;
316         bool is_stream;
317         char *base = NULL;
318         char *stream = NULL;
319
320         ret = onefs_is_stream(path, &base, &stream, &is_stream);
321         if (ret)
322                 return ret;
323
324         if (!is_stream) {
325                 ret = SMB_VFS_NEXT_STAT(handle, path, sbuf);
326         } else if (!stream) {
327                 /* If it's the ::$DATA stream just stat the base file name. */
328                 ret = SMB_VFS_NEXT_STAT(handle, base, sbuf);
329         } else {
330                 ret = stat_stream(handle, base, stream, sbuf, 0);
331         }
332
333         onefs_adjust_stat_time(handle, path, sbuf);
334         return ret;
335 }
336
337 int onefs_fstat(vfs_handle_struct *handle, struct files_struct *fsp,
338                 SMB_STRUCT_STAT *sbuf)
339 {
340         SMB_STRUCT_STAT base_sbuf;
341         int ret;
342
343         /* Stat the stream, by calling next_fstat on the stream's fd. */
344         ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
345         if (ret == -1) {
346                 return ret;
347         }
348
349         /* Stat the base file and merge the results. */
350         if (fsp != NULL && fsp->base_fsp != NULL) {
351                 ret = sys_fstat(fsp->base_fsp->fh->fd, &base_sbuf);
352                 if (ret != -1) {
353                         merge_stat(sbuf, &base_sbuf);
354                 }
355         }
356
357         onefs_adjust_stat_time(handle, fsp->fsp_name, sbuf);
358         return ret;
359 }
360
361 int onefs_lstat(vfs_handle_struct *handle, const char *path,
362                 SMB_STRUCT_STAT *sbuf)
363 {
364         int ret;
365         bool is_stream;
366         char *base = NULL;
367         char *stream = NULL;
368
369         ret = onefs_is_stream(path, &base, &stream, &is_stream);
370         if (ret)
371                 return ret;
372
373         if (!is_stream) {
374                 ret = SMB_VFS_NEXT_LSTAT(handle, path, sbuf);
375         } else if (!stream) {
376                 /* If it's the ::$DATA stream just stat the base file name. */
377                 ret = SMB_VFS_NEXT_LSTAT(handle, base, sbuf);
378         } else {
379                 ret = stat_stream(handle, base, stream, sbuf,
380                                   AT_SYMLINK_NOFOLLOW);
381         }
382
383         onefs_adjust_stat_time(handle, path, sbuf);
384         return ret;
385 }
386
387 int onefs_unlink(vfs_handle_struct *handle, const char *path)
388 {
389         int ret;
390         bool is_stream;
391         char *base = NULL;
392         char *stream = NULL;
393         int dir_fd, saved_errno;
394
395         ret = onefs_is_stream(path, &base, &stream, &is_stream);
396         if (ret) {
397                 return ret;
398         }
399
400         if (!is_stream) {
401                 return SMB_VFS_NEXT_UNLINK(handle, path);
402         }
403
404         /* If it's the ::$DATA stream just unlink the base file name. */
405         if (!stream) {
406                 return SMB_VFS_NEXT_UNLINK(handle, base);
407         }
408
409         dir_fd = get_stream_dir_fd(handle->conn, base, NULL);
410         if (dir_fd < 0) {
411                 return -1;
412         }
413
414         ret = enc_unlinkat(dir_fd, stream, ENC_DEFAULT, 0);
415
416         saved_errno = errno;
417         close(dir_fd);
418         errno = saved_errno;
419         return ret;
420 }
421
422 int onefs_vtimes_streams(vfs_handle_struct *handle, const char *fname,
423                          int flags, struct timespec times[3])
424 {
425         int ret;
426         bool is_stream;
427         char *base;
428         char *stream;
429         int dirfd;
430         int saved_errno;
431
432         START_PROFILE(syscall_ntimes);
433
434         ret = onefs_is_stream(fname, &base, &stream, &is_stream);
435         if (ret)
436                 return ret;
437
438         if (!is_stream) {
439                 ret = vtimes(fname, times, flags);
440                 return ret;
441         }
442
443         dirfd = get_stream_dir_fd(handle->conn, base, NULL);
444         if (dirfd < -1) {
445                 return -1;
446         }
447
448         ret = enc_vtimesat(dirfd, stream, ENC_DEFAULT, times, flags);
449
450         END_PROFILE(syscall_ntimes);
451
452         saved_errno = errno;
453         close(dirfd);
454         errno = saved_errno;
455         return ret;
456 }
457
458 int onefs_chflags(vfs_handle_struct *handle, const char *path,
459                   unsigned int flags)
460 {
461         char *base = NULL;
462         char *stream = NULL;
463
464         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
465                                                           &base, &stream))) {
466                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
467                 errno = ENOMEM;
468                 return -1;
469         }
470
471         /*
472          * Only set the attributes on the base file.  ifs_createfile handles
473          * file creation attribute semantics.
474          */
475         return SMB_VFS_NEXT_CHFLAGS(handle, base, flags);
476 }
477
478 /*
479  * Streaminfo enumeration functionality
480  */
481 struct streaminfo_state {
482         TALLOC_CTX *mem_ctx;
483         vfs_handle_struct *handle;
484         unsigned int num_streams;
485         struct stream_struct *streams;
486         NTSTATUS status;
487 };
488
489 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
490                            struct stream_struct **streams,
491                            const char *name, SMB_OFF_T size,
492                            SMB_OFF_T alloc_size)
493 {
494         struct stream_struct *tmp;
495
496         tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
497                                    (*num_streams)+1);
498         if (tmp == NULL) {
499                 return false;
500         }
501
502         tmp[*num_streams].name = talloc_asprintf(mem_ctx, ":%s:%s", name,
503                                                  "$DATA");
504         if (tmp[*num_streams].name == NULL) {
505                 return false;
506         }
507
508         tmp[*num_streams].size = size;
509         tmp[*num_streams].alloc_size = alloc_size;
510
511         *streams = tmp;
512         *num_streams += 1;
513         return true;
514 }
515
516 static NTSTATUS walk_onefs_streams(connection_struct *conn, files_struct *fsp,
517                                    const char *fname,
518                                    struct streaminfo_state *state,
519                                    SMB_STRUCT_STAT *base_sbuf)
520 {
521         NTSTATUS status = NT_STATUS_OK;
522         bool opened_base_fd = false;
523         int base_fd = -1;
524         int dir_fd = -1;
525         int stream_fd = -1;
526         int ret;
527         SMB_STRUCT_DIR *dirp = NULL;
528         SMB_STRUCT_DIRENT *dp = NULL;
529         files_struct fake_fs;
530         struct fd_handle fake_fh;
531         SMB_STRUCT_STAT stream_sbuf;
532
533         ZERO_STRUCT(fake_fh);
534         ZERO_STRUCT(fake_fs);
535
536         /* If the base file is already open, use its fd. */
537         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
538                 base_fd = fsp->fh->fd;
539         } else {
540                 opened_base_fd = true;
541         }
542
543         dir_fd = get_stream_dir_fd(conn, fname, &base_fd);
544         if (dir_fd < 0) {
545                 return map_nt_error_from_unix(errno);
546         }
547
548         /* Open the ADS directory. */
549         if ((dirp = fdopendir(dir_fd)) == NULL) {
550                 DEBUG(0, ("Error on opendir %s. errno=%d (%s)\n",
551                           fname, errno, strerror(errno)));
552                 status = map_nt_error_from_unix(errno);
553                 goto out;
554         }
555
556         /* Initialize the dir state struct and add it to the list.
557          * This is a layer violation, and really should be handled by a
558          * VFS_FDOPENDIR() call which would properly setup the dir state.
559          * But since this is all within the onefs.so module, we cheat for
560          * now and call directly into the readdirplus code.
561          * NOTE: This state MUST be freed by a proper VFS_CLOSEDIR() call. */
562         ret = onefs_rdp_add_dir_state(conn, dirp);
563         if (ret) {
564                 DEBUG(0, ("Error adding dir_state to the list\n"));
565                 status = map_nt_error_from_unix(errno);
566                 goto out;
567         }
568
569         fake_fs.conn = conn;
570         fake_fs.fh = &fake_fh;
571         fake_fs.fsp_name = SMB_STRDUP(fname);
572
573         /* Iterate over the streams in the ADS directory. */
574         while ((dp = SMB_VFS_READDIR(conn, dirp, NULL)) != NULL) {
575                 /* Skip the "." and ".." entries */
576                 if ((strcmp(dp->d_name, ".") == 0) ||
577                     (strcmp(dp->d_name, "..") == 0))
578                         continue;
579
580                 /* Open actual stream */
581                 if ((stream_fd = onefs_sys_create_file(conn,
582                                                          base_fd,
583                                                          dp->d_name,
584                                                          0,
585                                                          0,
586                                                          0,
587                                                          0,
588                                                          0,
589                                                          0,
590                                                          INTERNAL_OPEN_ONLY,
591                                                          0,
592                                                          NULL,
593                                                          0,
594                                                          NULL)) == -1) {
595                         DEBUG(0, ("Error opening stream %s:%s. "
596                                   "errno=%d (%s)\n", fname, dp->d_name, errno,
597                                   strerror(errno)));
598                         continue;
599                 }
600
601                 /* Figure out the stat info. */
602                 fake_fh.fd = stream_fd;
603                 ret = SMB_VFS_FSTAT(&fake_fs, &stream_sbuf);
604                 close(stream_fd);
605
606                 if (ret) {
607                         DEBUG(0, ("Error fstating stream %s:%s. "
608                                   "errno=%d (%s)\n", fname, dp->d_name, errno,
609                                   strerror(errno)));
610                         continue;
611                 }
612
613                 merge_stat(&stream_sbuf, base_sbuf);
614
615                 if (!add_one_stream(state->mem_ctx,
616                                     &state->num_streams, &state->streams,
617                                     dp->d_name, stream_sbuf.st_size,
618                                     SMB_VFS_GET_ALLOC_SIZE(conn, NULL,
619                                                            &stream_sbuf))) {
620                         state->status = NT_STATUS_NO_MEMORY;
621                         break;
622                 }
623         }
624
625 out:
626         /* Cleanup everything that was opened. */
627         if (dirp != NULL) {
628                 SMB_VFS_CLOSEDIR(conn, dirp);
629         }
630         if (dir_fd >= 0) {
631                 close(dir_fd);
632         }
633         if (opened_base_fd) {
634                 SMB_ASSERT(base_fd >= 0);
635                 close(base_fd);
636         }
637
638         SAFE_FREE(fake_fs.fsp_name);
639         return status;
640 }
641
642 NTSTATUS onefs_streaminfo(vfs_handle_struct *handle,
643                           struct files_struct *fsp,
644                           const char *fname,
645                           TALLOC_CTX *mem_ctx,
646                           unsigned int *num_streams,
647                           struct stream_struct **streams)
648 {
649         SMB_STRUCT_STAT sbuf;
650         int ret;
651         NTSTATUS status;
652         struct streaminfo_state state;
653
654         /* Get a valid stat. */
655         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
656                 if (is_ntfs_stream_name(fsp->fsp_name)) {
657                         return NT_STATUS_INVALID_PARAMETER;
658                 }
659                 ret = SMB_VFS_FSTAT(fsp, &sbuf);
660         } else {
661                 if (is_ntfs_stream_name(fname)) {
662                         return NT_STATUS_INVALID_PARAMETER;
663                 }
664                 ret = SMB_VFS_STAT(handle->conn, fname, &sbuf);
665         }
666
667         if (ret == -1) {
668                 return map_nt_error_from_unix(errno);
669         }
670
671         state.streams = NULL;
672         state.num_streams = 0;
673
674         if (lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE,
675                 PARM_IGNORE_STREAMS, PARM_IGNORE_STREAMS_DEFAULT)) {
676                 goto out;
677         }
678
679         /* Add the default stream. */
680         if (S_ISREG(sbuf.st_mode)) {
681                 if (!add_one_stream(mem_ctx,
682                                     &state.num_streams, &state.streams,
683                                     "", sbuf.st_size,
684                                     SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp,
685                                                            &sbuf))) {
686                         return NT_STATUS_NO_MEMORY;
687                 }
688         }
689
690         state.mem_ctx = mem_ctx;
691         state.handle = handle;
692         state.status = NT_STATUS_OK;
693
694         /* If there are more streams, add them too. */
695         if (sbuf.st_flags & UF_HASADS) {
696
697                 status = walk_onefs_streams(handle->conn, fsp, fname,
698                     &state, &sbuf);
699
700                 if (!NT_STATUS_IS_OK(status)) {
701                         TALLOC_FREE(state.streams);
702                         return status;
703                 }
704
705                 if (!NT_STATUS_IS_OK(state.status)) {
706                         TALLOC_FREE(state.streams);
707                         return state.status;
708                 }
709         }
710  out:
711         *num_streams = state.num_streams;
712         *streams = state.streams;
713         return NT_STATUS_OK;
714 }