Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-wsgi
[samba.git] / source4 / ntvfs / posix / pvfs_streams.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - alternate data streams
5
6    Copyright (C) Andrew Tridgell 2004
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 "vfs_posix.h"
24 #include "librpc/gen_ndr/xattr.h"
25
26
27 /*
28   return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION
29 */
30 NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs, 
31                                  TALLOC_CTX *mem_ctx,
32                                  struct pvfs_filename *name, int fd, 
33                                  struct stream_information *info)
34 {
35         struct xattr_DosStreams *streams;
36         int i;
37         NTSTATUS status;
38
39         /* directories don't have streams */
40         if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
41                 info->num_streams = 0;
42                 info->streams = NULL;
43                 return NT_STATUS_OK;
44         }
45
46         streams = talloc(mem_ctx, struct xattr_DosStreams);
47         if (streams == NULL) {
48                 return NT_STATUS_NO_MEMORY;
49         }
50
51         status = pvfs_streams_load(pvfs, name, fd, streams);
52         if (!NT_STATUS_IS_OK(status)) {
53                 ZERO_STRUCTP(streams);
54         }
55
56         info->num_streams = streams->num_streams+1;
57         info->streams = talloc_array(mem_ctx, struct stream_struct, info->num_streams);
58         if (!info->streams) {
59                 return NT_STATUS_NO_MEMORY;
60         }
61
62         info->streams[0].size          = name->st.st_size;
63         info->streams[0].alloc_size    = name->dos.alloc_size;
64         info->streams[0].stream_name.s = talloc_strdup(info->streams, "::$DATA");
65
66         for (i=0;i<streams->num_streams;i++) {
67                 info->streams[i+1].size          = streams->streams[i].size;
68                 info->streams[i+1].alloc_size    = streams->streams[i].alloc_size;
69                 info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams, 
70                                                                    ":%s:$DATA",
71                                                                    streams->streams[i].name);
72         }
73
74         return NT_STATUS_OK;
75 }
76
77
78 /*
79   fill in the stream information for a name
80 */
81 NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
82 {
83         struct xattr_DosStreams *streams;
84         int i;
85         NTSTATUS status;
86
87         /* the NULL stream always exists */
88         if (name->stream_name == NULL) {
89                 name->stream_exists = true;
90                 return NT_STATUS_OK;
91         }
92
93         streams = talloc(name, struct xattr_DosStreams);
94         if (streams == NULL) {
95                 return NT_STATUS_NO_MEMORY;
96         }
97
98         status = pvfs_streams_load(pvfs, name, fd, streams);
99         if (!NT_STATUS_IS_OK(status)) {
100                 talloc_free(streams);
101                 return status;
102         }
103
104         for (i=0;i<streams->num_streams;i++) {
105                 struct xattr_DosStream *s = &streams->streams[i];
106                 if (strcasecmp_m(s->name, name->stream_name) == 0) {
107                         name->dos.alloc_size = pvfs_round_alloc_size(pvfs, s->alloc_size);
108                         name->st.st_size     = s->size;
109                         name->stream_exists = true;
110                         talloc_free(streams);
111                         return NT_STATUS_OK;
112                 }
113         }
114
115         talloc_free(streams);
116
117         name->dos.alloc_size = 0;
118         name->st.st_size     = 0;
119         name->stream_exists = false;
120
121         return NT_STATUS_OK;
122 }
123
124
125 /*
126   update size information for a stream
127 */
128 static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
129                                         off_t size)
130 {
131         struct xattr_DosStreams *streams;
132         int i;
133         NTSTATUS status;
134
135         streams = talloc(name, struct xattr_DosStreams);
136         if (streams == NULL) {
137                 return NT_STATUS_NO_MEMORY;
138         }
139
140         status = pvfs_streams_load(pvfs, name, fd, streams);
141         if (!NT_STATUS_IS_OK(status)) {
142                 ZERO_STRUCTP(streams);
143         }
144
145         for (i=0;i<streams->num_streams;i++) {
146                 struct xattr_DosStream *s = &streams->streams[i];
147                 if (strcasecmp_m(s->name, name->stream_name) == 0) {
148                         s->size       = size;
149                         s->alloc_size = pvfs_round_alloc_size(pvfs, size);
150                         break;
151                 }
152         }
153
154         if (i == streams->num_streams) {
155                 struct xattr_DosStream *s;
156                 streams->streams = talloc_realloc(streams, streams->streams, 
157                                                     struct xattr_DosStream,
158                                                     streams->num_streams+1);
159                 if (streams->streams == NULL) {
160                         talloc_free(streams);
161                         return NT_STATUS_NO_MEMORY;
162                 }
163                 streams->num_streams++;
164                 s = &streams->streams[i];
165                 
166                 s->flags      = XATTR_STREAM_FLAG_INTERNAL;
167                 s->size       = size;
168                 s->alloc_size = pvfs_round_alloc_size(pvfs, size);
169                 s->name       = name->stream_name;
170         }
171
172         status = pvfs_streams_save(pvfs, name, fd, streams);
173         talloc_free(streams);
174
175         return status;
176 }
177
178
179 /*
180   create the xattr for a alternate data stream
181 */
182 NTSTATUS pvfs_stream_create(struct pvfs_state *pvfs, 
183                             struct pvfs_filename *name, 
184                             int fd)
185 {
186         NTSTATUS status;
187         status = pvfs_xattr_create(pvfs, name->full_name, fd, 
188                                    XATTR_DOSSTREAM_PREFIX, name->stream_name);
189         if (!NT_STATUS_IS_OK(status)) {
190                 return status;
191         }
192         return pvfs_stream_update_size(pvfs, name, fd, 0);
193 }
194
195 /*
196   delete the xattr for a alternate data stream
197 */
198 NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs, 
199                             struct pvfs_filename *name, 
200                             int fd)
201 {
202         NTSTATUS status;
203         struct xattr_DosStreams *streams;
204         int i;
205
206         status = pvfs_xattr_delete(pvfs, name->full_name, fd, 
207                                    XATTR_DOSSTREAM_PREFIX, name->stream_name);
208         if (!NT_STATUS_IS_OK(status)) {
209                 return status;
210         }
211
212         streams = talloc(name, struct xattr_DosStreams);
213         if (streams == NULL) {
214                 return NT_STATUS_NO_MEMORY;
215         }
216
217         status = pvfs_streams_load(pvfs, name, fd, streams);
218         if (!NT_STATUS_IS_OK(status)) {
219                 talloc_free(streams);
220                 return status;
221         }
222
223         for (i=0;i<streams->num_streams;i++) {
224                 struct xattr_DosStream *s = &streams->streams[i];
225                 if (strcasecmp_m(s->name, name->stream_name) == 0) {
226                         memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s));
227                         streams->num_streams--;
228                         break;
229                 }
230         }
231
232         status = pvfs_streams_save(pvfs, name, fd, streams);
233         talloc_free(streams);
234
235         return status;
236 }
237
238 /*
239   the equvalent of pread() on a stream
240 */
241 ssize_t pvfs_stream_read(struct pvfs_state *pvfs,
242                          struct pvfs_file_handle *h, void *data, size_t count, off_t offset)
243 {
244         NTSTATUS status;
245         DATA_BLOB blob;
246         if (count == 0) {
247                 return 0;
248         }
249         status = pvfs_xattr_load(pvfs, h, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
250                                  h->name->stream_name, offset+count, &blob);
251         if (!NT_STATUS_IS_OK(status)) {
252                 errno = EIO;
253                 return -1;
254         }
255         if (offset >= blob.length) {
256                 data_blob_free(&blob);
257                 return 0;
258         }
259         if (count > blob.length - offset) {
260                 count = blob.length - offset;
261         }
262         memcpy(data, blob.data + offset, count);
263         data_blob_free(&blob);
264         return count;
265 }
266
267
268 /*
269   the equvalent of pwrite() on a stream
270 */
271 ssize_t pvfs_stream_write(struct pvfs_state *pvfs,
272                           struct pvfs_file_handle *h, const void *data, size_t count, off_t offset)
273 {
274         NTSTATUS status;
275         DATA_BLOB blob;
276         if (count == 0) {
277                 return 0;
278         }
279         if (offset > XATTR_MAX_STREAM_SIZE) {
280                 errno = ENOSPC;
281                 return -1;
282         }
283
284         /* we have to load the existing stream, then modify, then save */
285         status = pvfs_xattr_load(pvfs, h, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
286                                  h->name->stream_name, offset+count, &blob);
287         if (!NT_STATUS_IS_OK(status)) {
288                 blob = data_blob(NULL, 0);
289         }
290         if (count+offset > blob.length) {
291                 blob.data = talloc_realloc(blob.data, blob.data, uint8_t, count+offset);
292                 if (blob.data == NULL) {
293                         errno = ENOMEM;
294                         return -1;
295                 }
296                 if (offset > blob.length) {
297                         memset(blob.data+blob.length, 0, offset - blob.length);
298                 }
299                 blob.length = count+offset;
300         }
301         memcpy(blob.data + offset, data, count);
302
303         status = pvfs_xattr_save(pvfs, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
304                                  h->name->stream_name, &blob);
305         if (!NT_STATUS_IS_OK(status)) {
306                 data_blob_free(&blob);
307                 /* getting this error mapping right is probably
308                    not worth it */
309                 errno = ENOSPC;
310                 return -1;
311         }
312
313         status = pvfs_stream_update_size(pvfs, h->name, h->fd, blob.length);
314
315         data_blob_free(&blob);
316
317         if (!NT_STATUS_IS_OK(status)) {
318                 errno = EIO;
319                 return -1;
320         }
321
322         return count;
323 }
324
325 /*
326   the equvalent of truncate() on a stream
327 */
328 NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs,
329                               struct pvfs_filename *name, int fd, off_t length)
330 {
331         NTSTATUS status;
332         DATA_BLOB blob;
333
334         if (length > XATTR_MAX_STREAM_SIZE) {
335                 return NT_STATUS_DISK_FULL;
336         }
337
338         /* we have to load the existing stream, then modify, then save */
339         status = pvfs_xattr_load(pvfs, name, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
340                                  name->stream_name, length, &blob);
341         if (!NT_STATUS_IS_OK(status)) {
342                 return status;
343         }
344         if (length <= blob.length) {
345                 blob.length = length;
346         } else if (length > blob.length) {
347                 blob.data = talloc_realloc(blob.data, blob.data, uint8_t, length);
348                 if (blob.data == NULL) {
349                         return NT_STATUS_NO_MEMORY;
350                 }
351                 memset(blob.data+blob.length, 0, length - blob.length);
352                 blob.length = length;
353         }
354
355         status = pvfs_xattr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
356                                  name->stream_name, &blob);
357         data_blob_free(&blob);
358
359         if (NT_STATUS_IS_OK(status)) {
360                 status = pvfs_stream_update_size(pvfs, name, fd, blob.length);
361         }
362
363         return status;
364 }