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