184fe4f0c99a4edc38c7422739bd0b0bcdb85644
[metze/samba/wip.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_close(vfs_handle_struct *handle, struct files_struct *fsp)
55 {
56         int ret2, ret = 0;
57
58         if (fsp->base_fsp) {
59                 ret = SMB_VFS_NEXT_CLOSE(handle, fsp->base_fsp);
60         }
61         ret2 = SMB_VFS_NEXT_CLOSE(handle, fsp);
62
63         return ret ? ret : ret2;
64 }
65
66 /*
67  * Get the ADS directory fd for a file.
68  */
69 static int get_stream_dir_fd(connection_struct *conn, const char *base,
70                              int *base_fdp)
71 {
72         int base_fd;
73         int dir_fd;
74         int saved_errno;
75
76         /* If a valid base_fdp was given, use it. */
77         if (base_fdp && *base_fdp >= 0) {
78                 base_fd = *base_fdp;
79         } else {
80                 base_fd = onefs_sys_create_file(conn,
81                                                 -1,
82                                                 base,
83                                                 0,
84                                                 0,
85                                                 0,
86                                                 0,
87                                                 0,
88                                                 0,
89                                                 INTERNAL_OPEN_ONLY,
90                                                 0,
91                                                 NULL,
92                                                 0,
93                                                 NULL);
94                 if (base_fd < 0) {
95                         return -1;
96                 }
97         }
98
99         /* Open the ADS directory. */
100         dir_fd = onefs_sys_create_file(conn,
101                                         base_fd,
102                                         ".",
103                                         0,
104                                         FILE_READ_DATA,
105                                         0,
106                                         0,
107                                         0,
108                                         0,
109                                         INTERNAL_OPEN_ONLY,
110                                         0,
111                                         NULL,
112                                         0,
113                                         NULL);
114
115         /* Close base_fd if it's not need or on error. */
116         if (!base_fdp || dir_fd < 0) {
117                 saved_errno = errno;
118                 close(base_fd);
119                 errno = saved_errno;
120         }
121
122         /* Set the out base_fdp if successful and it was requested. */
123         if (base_fdp && dir_fd >= 0) {
124                 *base_fdp = base_fd;
125         }
126
127         return dir_fd;
128 }
129
130 int onefs_rename(vfs_handle_struct *handle, const char *oldname,
131                  const char *newname)
132 {
133         TALLOC_CTX *frame = NULL;
134         int ret = -1;
135         int dir_fd = -1;
136         int saved_errno;
137         bool old_is_stream;
138         bool new_is_stream;
139         char *obase = NULL;
140         char *osname = NULL;
141         char *nbase = NULL;
142         char *nsname = NULL;
143
144         old_is_stream = is_ntfs_stream_name(oldname);
145         new_is_stream = is_ntfs_stream_name(newname);
146
147         if (!old_is_stream && !new_is_stream) {
148                 return SMB_VFS_NEXT_RENAME(handle, oldname, newname);
149         }
150
151         frame = talloc_stackframe();
152
153         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(),
154                                                           oldname, &obase,
155                                                           &osname))) {
156                 errno = ENOMEM;
157                 goto done;
158         }
159
160         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(),
161                                                           newname, &nbase,
162                                                           &nsname))) {
163                 errno = ENOMEM;
164                 goto done;
165         }
166
167         dir_fd = get_stream_dir_fd(handle->conn, obase, NULL);
168         if (dir_fd < -1) {
169                 goto done;
170         }
171
172         DEBUG(8,("onefs_rename called for %s : %s  => %s : %s\n",
173                 obase, osname,  nbase, nsname));
174
175         /* Handle rename of stream to default stream specially. */
176         if (nsname == NULL) {
177                 ret = enc_renameat(dir_fd, osname, ENC_DEFAULT, AT_FDCWD,
178                                    nbase, ENC_DEFAULT);
179         } else {
180                 ret = enc_renameat(dir_fd, osname, ENC_DEFAULT, dir_fd, nsname,
181                                    ENC_DEFAULT);
182         }
183
184  done:
185         saved_errno = errno;
186         if (dir_fd >= 0) {
187                 close(dir_fd);
188         }
189         errno = saved_errno;
190         TALLOC_FREE(frame);
191         return ret;
192 }
193
194 /*
195  * Merge a base file's sbuf into the a streams's sbuf.
196  */
197 static void merge_stat(SMB_STRUCT_STAT *stream_sbuf,
198                        const SMB_STRUCT_STAT *base_sbuf)
199 {
200         int dos_flags = (UF_DOS_NOINDEX | UF_DOS_ARCHIVE |
201             UF_DOS_HIDDEN | UF_DOS_RO | UF_DOS_SYSTEM);
202         stream_sbuf->st_mtime = base_sbuf->st_mtime;
203         stream_sbuf->st_ctime = base_sbuf->st_ctime;
204         stream_sbuf->st_atime = base_sbuf->st_atime;
205         stream_sbuf->st_flags &= ~dos_flags;
206         stream_sbuf->st_flags |= base_sbuf->st_flags & dos_flags;
207 }
208
209 static int stat_stream(vfs_handle_struct *handle, const char *base,
210                        const char *stream, SMB_STRUCT_STAT *sbuf, int flags)
211 {
212         SMB_STRUCT_STAT base_sbuf;
213         int base_fd = -1, dir_fd, ret, saved_errno;
214
215         dir_fd = get_stream_dir_fd(handle->conn, base, &base_fd);
216         if (dir_fd < 0) {
217                 return -1;
218         }
219
220         /* Stat the stream. */
221         ret = enc_fstatat(dir_fd, stream, ENC_DEFAULT, sbuf, flags);
222         if (ret != -1) {
223                 /* Now stat the base file and merge the results. */
224                 ret = sys_fstat(base_fd, &base_sbuf);
225                 if (ret != -1) {
226                         merge_stat(sbuf, &base_sbuf);
227                 }
228         }
229
230         saved_errno = errno;
231         close(dir_fd);
232         close(base_fd);
233         errno = saved_errno;
234         return ret;
235 }
236
237 int onefs_stat(vfs_handle_struct *handle, const char *path,
238                SMB_STRUCT_STAT *sbuf)
239 {
240         char *base = NULL;
241         char *stream = NULL;
242
243         if (!is_ntfs_stream_name(path)) {
244                 return SMB_VFS_NEXT_STAT(handle, path, sbuf);
245         }
246
247         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
248                                                           &base, &stream))) {
249                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
250                 errno = ENOMEM;
251                 return -1;
252         }
253
254         /* If it's the ::$DATA stream just stat the base file name. */
255         if (!stream) {
256                 return SMB_VFS_NEXT_STAT(handle, base, sbuf);
257         }
258
259         return stat_stream(handle, base, stream, sbuf, 0);
260 }
261
262 int onefs_fstat(vfs_handle_struct *handle, struct files_struct *fsp,
263                 SMB_STRUCT_STAT *sbuf)
264 {
265         SMB_STRUCT_STAT base_sbuf;
266         int ret;
267
268         /* Stat the stream, by calling next_fstat on the stream's fd. */
269         ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
270         if (ret == -1) {
271                 return ret;
272         }
273
274         /* Stat the base file and merge the results. */
275         if (fsp != NULL && fsp->base_fsp != NULL) {
276                 ret = sys_fstat(fsp->base_fsp->fh->fd, &base_sbuf);
277                 if (ret != -1) {
278                         merge_stat(sbuf, &base_sbuf);
279                 }
280         }
281
282         return ret;
283 }
284
285 int onefs_lstat(vfs_handle_struct *handle, const char *path,
286                 SMB_STRUCT_STAT *sbuf)
287 {
288         char *base = NULL;
289         char *stream = NULL;
290
291         if (!is_ntfs_stream_name(path)) {
292                 return SMB_VFS_NEXT_LSTAT(handle, path, sbuf);
293         }
294
295         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
296                                                           &base, &stream))) {
297                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
298                 errno = ENOMEM;
299                 return -1;
300         }
301
302         /* If it's the ::$DATA stream just stat the base file name. */
303         if (!stream) {
304                 return SMB_VFS_NEXT_LSTAT(handle, base, sbuf);
305         }
306
307         return stat_stream(handle, base, stream, sbuf, AT_SYMLINK_NOFOLLOW);
308 }
309
310 int onefs_unlink(vfs_handle_struct *handle, const char *path)
311 {
312         char *base = NULL;
313         char *stream = NULL;
314         int dir_fd, ret, saved_errno;
315
316         if (!is_ntfs_stream_name(path)) {
317                 return SMB_VFS_NEXT_UNLINK(handle, path);
318         }
319
320         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
321                                                           &base, &stream))) {
322                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
323                 errno = ENOMEM;
324                 return -1;
325         }
326
327         /* If it's the ::$DATA stream just unlink the base file name. */
328         if (!stream) {
329                 return SMB_VFS_NEXT_UNLINK(handle, base);
330         }
331
332         dir_fd = get_stream_dir_fd(handle->conn, base, NULL);
333         if (dir_fd < 0) {
334                 return -1;
335         }
336
337         ret = enc_unlinkat(dir_fd, stream, ENC_DEFAULT, 0);
338
339         saved_errno = errno;
340         close(dir_fd);
341         errno = saved_errno;
342         return ret;
343 }
344
345 int onefs_chflags(vfs_handle_struct *handle, const char *path,
346                   unsigned int flags)
347 {
348         char *base = NULL;
349         char *stream = NULL;
350
351         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
352                                                           &base, &stream))) {
353                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
354                 errno = ENOMEM;
355                 return -1;
356         }
357
358         /*
359          * Only set the attributes on the base file.  ifs_createfile handles
360          * file creation attribute semantics.
361          */
362         return SMB_VFS_NEXT_CHFLAGS(handle, base, flags);
363 }
364
365 /*
366  * Streaminfo enumeration functionality
367  */
368 struct streaminfo_state {
369         TALLOC_CTX *mem_ctx;
370         vfs_handle_struct *handle;
371         unsigned int num_streams;
372         struct stream_struct *streams;
373         NTSTATUS status;
374 };
375
376 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
377                            struct stream_struct **streams,
378                            const char *name, SMB_OFF_T size,
379                            SMB_OFF_T alloc_size)
380 {
381         struct stream_struct *tmp;
382
383         tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
384                                    (*num_streams)+1);
385         if (tmp == NULL) {
386                 return false;
387         }
388
389         tmp[*num_streams].name = talloc_asprintf(mem_ctx, ":%s:%s", name,
390                                                  "$DATA");
391         if (tmp[*num_streams].name == NULL) {
392                 return false;
393         }
394
395         tmp[*num_streams].size = size;
396         tmp[*num_streams].alloc_size = alloc_size;
397
398         *streams = tmp;
399         *num_streams += 1;
400         return true;
401 }
402
403 static NTSTATUS walk_onefs_streams(connection_struct *conn, files_struct *fsp,
404                                    const char *fname,
405                                    struct streaminfo_state *state,
406                                    SMB_STRUCT_STAT *base_sbuf)
407 {
408         NTSTATUS status = NT_STATUS_OK;
409         bool opened_base_fd = false;
410         int base_fd = -1;
411         int dir_fd = -1;
412         int stream_fd = -1;
413         int ret;
414         SMB_STRUCT_DIR *dirp = NULL;
415         SMB_STRUCT_DIRENT *dp = NULL;
416         files_struct fake_fs;
417         struct fd_handle fake_fh;
418         SMB_STRUCT_STAT stream_sbuf;
419
420         ZERO_STRUCT(fake_fh);
421         ZERO_STRUCT(fake_fs);
422
423         /* If the base file is already open, use its fd. */
424         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
425                 base_fd = fsp->fh->fd;
426         } else {
427                 opened_base_fd = true;
428         }
429
430         dir_fd = get_stream_dir_fd(conn, fname, &base_fd);
431         if (dir_fd < 0) {
432                 return map_nt_error_from_unix(errno);
433         }
434
435         /* Open the ADS directory. */
436         if ((dirp = fdopendir(dir_fd)) == NULL) {
437                 DEBUG(0, ("Error on opendir %s. errno=%d (%s)\n",
438                           fname, errno, strerror(errno)));
439                 status = map_nt_error_from_unix(errno);
440                 goto out;
441         }
442
443         fake_fs.conn = conn;
444         fake_fs.fh = &fake_fh;
445         fake_fs.fsp_name = SMB_STRDUP(fname);
446
447         /* Iterate over the streams in the ADS directory. */
448         while ((dp = SMB_VFS_READDIR(conn, dirp)) != NULL) {
449                 /* Skip the "." and ".." entries */
450                 if ((strcmp(dp->d_name, ".") == 0) ||
451                     (strcmp(dp->d_name, "..") == 0))
452                         continue;
453
454                 /* Open actual stream */
455                 if ((stream_fd = onefs_sys_create_file(conn,
456                                                          base_fd,
457                                                          dp->d_name,
458                                                          0,
459                                                          0,
460                                                          0,
461                                                          0,
462                                                          0,
463                                                          0,
464                                                          INTERNAL_OPEN_ONLY,
465                                                          0,
466                                                          NULL,
467                                                          0,
468                                                          NULL)) == -1) {
469                         DEBUG(0, ("Error opening stream %s:%s. "
470                                   "errno=%d (%s)\n", fname, dp->d_name, errno,
471                                   strerror(errno)));
472                         continue;
473                 }
474
475                 /* Figure out the stat info. */
476                 fake_fh.fd = stream_fd;
477                 ret = SMB_VFS_FSTAT(&fake_fs, &stream_sbuf);
478                 close(stream_fd);
479
480                 if (ret) {
481                         DEBUG(0, ("Error fstating stream %s:%s. "
482                                   "errno=%d (%s)\n", fname, dp->d_name, errno,
483                                   strerror(errno)));
484                         continue;
485                 }
486
487                 merge_stat(&stream_sbuf, base_sbuf);
488
489                 if (!add_one_stream(state->mem_ctx,
490                                     &state->num_streams, &state->streams,
491                                     dp->d_name, stream_sbuf.st_size,
492                                     get_allocation_size(conn, NULL,
493                                                         &stream_sbuf))) {
494                         state->status = NT_STATUS_NO_MEMORY;
495                         break;
496                 }
497         }
498
499 out:
500         /* Cleanup everything that was opened. */
501         if (dirp != NULL) {
502                 SMB_VFS_CLOSEDIR(conn, dirp);
503         }
504         if (dir_fd >= 0) {
505                 close(dir_fd);
506         }
507         if (opened_base_fd) {
508                 SMB_ASSERT(base_fd >= 0);
509                 close(base_fd);
510         }
511
512         SAFE_FREE(fake_fs.fsp_name);
513         return status;
514 }
515
516 NTSTATUS onefs_streaminfo(vfs_handle_struct *handle,
517                           struct files_struct *fsp,
518                           const char *fname,
519                           TALLOC_CTX *mem_ctx,
520                           unsigned int *num_streams,
521                           struct stream_struct **streams)
522 {
523         SMB_STRUCT_STAT sbuf;
524         int ret;
525         NTSTATUS status;
526         struct streaminfo_state state;
527
528         /* Get a valid stat. */
529         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
530                 if (is_ntfs_stream_name(fsp->fsp_name)) {
531                         return NT_STATUS_INVALID_PARAMETER;
532                 }
533                 ret = SMB_VFS_FSTAT(fsp, &sbuf);
534         } else {
535                 if (is_ntfs_stream_name(fname)) {
536                         return NT_STATUS_INVALID_PARAMETER;
537                 }
538                 ret = SMB_VFS_STAT(handle->conn, fname, &sbuf);
539         }
540
541         if (ret == -1) {
542                 return map_nt_error_from_unix(errno);
543         }
544
545         state.streams = NULL;
546         state.num_streams = 0;
547
548         /* Add the default stream. */
549         if (S_ISREG(sbuf.st_mode)) {
550                 if (!add_one_stream(mem_ctx,
551                                     &state.num_streams, &state.streams,
552                                     "", sbuf.st_size,
553                                     get_allocation_size(handle->conn, fsp,
554                                                         &sbuf))) {
555                         return NT_STATUS_NO_MEMORY;
556                 }
557         }
558
559         state.mem_ctx = mem_ctx;
560         state.handle = handle;
561         state.status = NT_STATUS_OK;
562
563         /* If there are more streams, add them too. */
564         if (sbuf.st_flags & UF_HASADS) {
565
566                 status = walk_onefs_streams(handle->conn, fsp, fname,
567                     &state, &sbuf);
568
569                 if (!NT_STATUS_IS_OK(status)) {
570                         TALLOC_FREE(state.streams);
571                         return status;
572                 }
573
574                 if (!NT_STATUS_IS_OK(state.status)) {
575                         TALLOC_FREE(state.streams);
576                         return state.status;
577                 }
578         }
579
580         *num_streams = state.num_streams;
581         *streams = state.streams;
582         return NT_STATUS_OK;
583 }