Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : simple NTVFS filesystem backend
5 :
6 : Copyright (C) Andrew Tridgell 2003
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 : this implements a very simple NTVFS filesystem backend.
23 :
24 : this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
25 : minimal work to give a working backend.
26 : */
27 :
28 : #include "includes.h"
29 : #include "system/dir.h"
30 : #include "system/filesys.h"
31 : #include "svfs.h"
32 : #include "system/time.h"
33 : #include "../lib/util/dlinklist.h"
34 : #include "ntvfs/ntvfs.h"
35 : #include "ntvfs/simple/proto.h"
36 :
37 : #ifndef O_DIRECTORY
38 : #define O_DIRECTORY 0
39 : #endif
40 :
41 : #define CHECK_READ_ONLY(req) do { if (share_bool_option(ntvfs->ctx->config, SHARE_READONLY, true)) return NT_STATUS_ACCESS_DENIED; } while (0)
42 :
43 : /*
44 : connect to a share - used when a tree_connect operation comes
45 : in. For a disk based backend we needs to ensure that the base
46 : directory exists (tho it doesn't need to be accessible by the user,
47 : that comes later)
48 : */
49 2 : static NTSTATUS svfs_connect(struct ntvfs_module_context *ntvfs,
50 : struct ntvfs_request *req,
51 : union smb_tcon* tcon)
52 : {
53 0 : struct stat st;
54 0 : struct svfs_private *p;
55 2 : struct share_config *scfg = ntvfs->ctx->config;
56 0 : const char *sharename;
57 :
58 2 : switch (tcon->generic.level) {
59 0 : case RAW_TCON_TCON:
60 0 : sharename = tcon->tcon.in.service;
61 0 : break;
62 2 : case RAW_TCON_TCONX:
63 2 : sharename = tcon->tconx.in.path;
64 2 : break;
65 0 : case RAW_TCON_SMB2:
66 0 : sharename = tcon->smb2.in.path;
67 0 : break;
68 0 : default:
69 0 : return NT_STATUS_INVALID_LEVEL;
70 : }
71 :
72 2 : if (strncmp(sharename, "\\\\", 2) == 0) {
73 2 : char *p2 = strchr(sharename+2, '\\');
74 2 : if (p2) {
75 2 : sharename = p2 + 1;
76 : }
77 : }
78 :
79 2 : p = talloc(ntvfs, struct svfs_private);
80 2 : NT_STATUS_HAVE_NO_MEMORY(p);
81 2 : p->ntvfs = ntvfs;
82 2 : p->next_search_handle = 0;
83 2 : p->connectpath = share_string_option(p, scfg, SHARE_PATH, "");
84 2 : p->open_files = NULL;
85 2 : p->search = NULL;
86 :
87 : /* the directory must exist */
88 2 : if (stat(p->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
89 0 : DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n",
90 : p->connectpath, sharename));
91 0 : return NT_STATUS_BAD_NETWORK_NAME;
92 : }
93 :
94 2 : ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
95 2 : NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
96 2 : ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
97 2 : NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
98 :
99 2 : if (tcon->generic.level == RAW_TCON_TCONX) {
100 2 : tcon->tconx.out.fs_type = ntvfs->ctx->fs_type;
101 2 : tcon->tconx.out.dev_type = ntvfs->ctx->dev_type;
102 : }
103 :
104 2 : ntvfs->private_data = p;
105 :
106 2 : return NT_STATUS_OK;
107 : }
108 :
109 : /*
110 : disconnect from a share
111 : */
112 2 : static NTSTATUS svfs_disconnect(struct ntvfs_module_context *ntvfs)
113 : {
114 2 : return NT_STATUS_OK;
115 : }
116 :
117 : /*
118 : find open file handle given fd
119 : */
120 102 : static struct svfs_file *find_fd(struct svfs_private *sp, struct ntvfs_handle *handle)
121 : {
122 0 : struct svfs_file *f;
123 0 : void *p;
124 :
125 102 : p = ntvfs_handle_get_backend_data(handle, sp->ntvfs);
126 102 : if (!p) return NULL;
127 :
128 102 : f = talloc_get_type(p, struct svfs_file);
129 102 : if (!f) return NULL;
130 :
131 102 : return f;
132 : }
133 :
134 : /*
135 : delete a file - the dirtype specifies the file types to include in the search.
136 : The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
137 : */
138 4 : static NTSTATUS svfs_unlink(struct ntvfs_module_context *ntvfs,
139 : struct ntvfs_request *req,
140 : union smb_unlink *unl)
141 : {
142 0 : char *unix_path;
143 :
144 4 : CHECK_READ_ONLY(req);
145 :
146 4 : unix_path = svfs_unix_path(ntvfs, req, unl->unlink.in.pattern);
147 :
148 : /* ignoring wildcards ... */
149 4 : if (unlink(unix_path) == -1) {
150 2 : return map_nt_error_from_unix_common(errno);
151 : }
152 :
153 2 : return NT_STATUS_OK;
154 : }
155 :
156 :
157 : /*
158 : ioctl interface - we don't do any
159 : */
160 0 : static NTSTATUS svfs_ioctl(struct ntvfs_module_context *ntvfs,
161 : struct ntvfs_request *req, union smb_ioctl *io)
162 : {
163 0 : return NT_STATUS_INVALID_PARAMETER;
164 : }
165 :
166 : /*
167 : check if a directory exists
168 : */
169 0 : static NTSTATUS svfs_chkpath(struct ntvfs_module_context *ntvfs,
170 : struct ntvfs_request *req,
171 : union smb_chkpath *cp)
172 : {
173 0 : char *unix_path;
174 0 : struct stat st;
175 :
176 0 : unix_path = svfs_unix_path(ntvfs, req, cp->chkpath.in.path);
177 :
178 0 : if (stat(unix_path, &st) == -1) {
179 0 : return map_nt_error_from_unix_common(errno);
180 : }
181 :
182 0 : if (!S_ISDIR(st.st_mode)) {
183 0 : return NT_STATUS_NOT_A_DIRECTORY;
184 : }
185 :
186 0 : return NT_STATUS_OK;
187 : }
188 :
189 : /*
190 : build a file_id from a stat struct
191 : */
192 0 : static uint64_t svfs_file_id(struct stat *st)
193 : {
194 0 : uint64_t ret = st->st_ino;
195 0 : ret <<= 32;
196 0 : ret |= st->st_dev;
197 0 : return ret;
198 : }
199 :
200 : /*
201 : approximately map a struct stat to a generic fileinfo struct
202 : */
203 0 : static NTSTATUS svfs_map_fileinfo(struct ntvfs_module_context *ntvfs,
204 : struct ntvfs_request *req, union smb_fileinfo *info,
205 : struct stat *st, const char *unix_path)
206 : {
207 0 : struct svfs_dir *dir = NULL;
208 0 : char *pattern = NULL;
209 0 : int i, ret;
210 0 : const char *s, *short_name;
211 :
212 0 : s = strrchr(unix_path, '/');
213 0 : if (s) {
214 0 : short_name = s+1;
215 : } else {
216 0 : short_name = "";
217 : }
218 :
219 0 : ret = asprintf(&pattern, "%s:*", unix_path);
220 0 : if (ret == -1) {
221 0 : return NT_STATUS_NO_MEMORY;
222 : }
223 :
224 0 : if (pattern) {
225 0 : dir = svfs_list_unix(req, req, pattern);
226 : }
227 :
228 0 : unix_to_nt_time(&info->generic.out.create_time, st->st_ctime);
229 0 : unix_to_nt_time(&info->generic.out.access_time, st->st_atime);
230 0 : unix_to_nt_time(&info->generic.out.write_time, st->st_mtime);
231 0 : unix_to_nt_time(&info->generic.out.change_time, st->st_mtime);
232 0 : info->generic.out.alloc_size = st->st_size;
233 0 : info->generic.out.size = st->st_size;
234 0 : info->generic.out.attrib = svfs_unix_to_dos_attrib(st->st_mode);
235 0 : info->generic.out.alloc_size = st->st_blksize * st->st_blocks;
236 0 : info->generic.out.nlink = st->st_nlink;
237 0 : info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
238 0 : info->generic.out.file_id = svfs_file_id(st);
239 : /* REWRITE: TODO stuff in here */
240 0 : info->generic.out.delete_pending = 0;
241 0 : info->generic.out.ea_size = 0;
242 0 : info->generic.out.num_eas = 0;
243 0 : info->generic.out.fname.s = talloc_strdup(req, short_name);
244 0 : info->generic.out.alt_fname.s = talloc_strdup(req, short_name);
245 0 : info->generic.out.compressed_size = 0;
246 0 : info->generic.out.format = 0;
247 0 : info->generic.out.unit_shift = 0;
248 0 : info->generic.out.chunk_shift = 0;
249 0 : info->generic.out.cluster_shift = 0;
250 :
251 0 : info->generic.out.access_flags = 0;
252 0 : info->generic.out.position = 0;
253 0 : info->generic.out.mode = 0;
254 0 : info->generic.out.alignment_requirement = 0;
255 0 : info->generic.out.reparse_tag = 0;
256 0 : info->generic.out.num_streams = 0;
257 : /* setup a single data stream */
258 0 : info->generic.out.num_streams = 1 + (dir?dir->count:0);
259 0 : info->generic.out.streams = talloc_array(req,
260 : struct stream_struct,
261 : info->generic.out.num_streams);
262 0 : if (!info->generic.out.streams) {
263 0 : return NT_STATUS_NO_MEMORY;
264 : }
265 0 : info->generic.out.streams[0].size = st->st_size;
266 0 : info->generic.out.streams[0].alloc_size = st->st_size;
267 0 : info->generic.out.streams[0].stream_name.s = talloc_strdup(req,"::$DATA");
268 :
269 0 : for (i=0;dir && i<dir->count;i++) {
270 0 : s = strchr(dir->files[i].name, ':');
271 0 : info->generic.out.streams[1+i].size = dir->files[i].st.st_size;
272 0 : info->generic.out.streams[1+i].alloc_size = dir->files[i].st.st_size;
273 0 : info->generic.out.streams[1+i].stream_name.s = s?s:dir->files[i].name;
274 : }
275 :
276 0 : return NT_STATUS_OK;
277 : }
278 :
279 : /*
280 : return info on a pathname
281 : */
282 0 : static NTSTATUS svfs_qpathinfo(struct ntvfs_module_context *ntvfs,
283 : struct ntvfs_request *req, union smb_fileinfo *info)
284 : {
285 0 : char *unix_path;
286 0 : struct stat st;
287 :
288 0 : DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.file.path, info->generic.level));
289 0 : if (info->generic.level != RAW_FILEINFO_GENERIC) {
290 0 : return ntvfs_map_qpathinfo(ntvfs, req, info);
291 : }
292 :
293 0 : unix_path = svfs_unix_path(ntvfs, req, info->generic.in.file.path);
294 0 : DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path));
295 0 : if (stat(unix_path, &st) == -1) {
296 0 : DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno));
297 0 : return map_nt_error_from_unix_common(errno);
298 : }
299 0 : DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path));
300 0 : return svfs_map_fileinfo(ntvfs, req, info, &st, unix_path);
301 : }
302 :
303 : /*
304 : query info on a open file
305 : */
306 0 : static NTSTATUS svfs_qfileinfo(struct ntvfs_module_context *ntvfs,
307 : struct ntvfs_request *req, union smb_fileinfo *info)
308 : {
309 0 : struct svfs_private *p = ntvfs->private_data;
310 0 : struct svfs_file *f;
311 0 : struct stat st;
312 :
313 0 : if (info->generic.level != RAW_FILEINFO_GENERIC) {
314 0 : return ntvfs_map_qfileinfo(ntvfs, req, info);
315 : }
316 :
317 0 : f = find_fd(p, info->generic.in.file.ntvfs);
318 0 : if (!f) {
319 0 : return NT_STATUS_INVALID_HANDLE;
320 : }
321 :
322 0 : if (fstat(f->fd, &st) == -1) {
323 0 : return map_nt_error_from_unix_common(errno);
324 : }
325 :
326 0 : return svfs_map_fileinfo(ntvfs, req,info, &st, f->name);
327 : }
328 :
329 :
330 : /*
331 : open a file
332 : */
333 8 : static NTSTATUS svfs_open(struct ntvfs_module_context *ntvfs,
334 : struct ntvfs_request *req, union smb_open *io)
335 : {
336 8 : struct svfs_private *p = ntvfs->private_data;
337 0 : char *unix_path;
338 0 : struct stat st;
339 0 : int fd, flags;
340 0 : struct svfs_file *f;
341 0 : int create_flags, rdwr_flags;
342 0 : bool readonly;
343 0 : NTSTATUS status;
344 0 : struct ntvfs_handle *handle;
345 :
346 8 : if (io->generic.level != RAW_OPEN_GENERIC) {
347 4 : return ntvfs_map_open(ntvfs, req, io);
348 : }
349 :
350 4 : readonly = share_bool_option(ntvfs->ctx->config, SHARE_READONLY, SHARE_READONLY_DEFAULT);
351 4 : if (readonly) {
352 0 : create_flags = 0;
353 0 : rdwr_flags = O_RDONLY;
354 : } else {
355 4 : create_flags = O_CREAT;
356 4 : rdwr_flags = O_RDWR;
357 : }
358 :
359 4 : unix_path = svfs_unix_path(ntvfs, req, io->ntcreatex.in.fname);
360 :
361 4 : switch (io->generic.in.open_disposition) {
362 0 : case NTCREATEX_DISP_SUPERSEDE:
363 : case NTCREATEX_DISP_OVERWRITE_IF:
364 0 : flags = create_flags | O_TRUNC;
365 0 : break;
366 2 : case NTCREATEX_DISP_OPEN:
367 : case NTCREATEX_DISP_OVERWRITE:
368 2 : flags = 0;
369 2 : break;
370 2 : case NTCREATEX_DISP_CREATE:
371 2 : flags = create_flags | O_EXCL;
372 2 : break;
373 0 : case NTCREATEX_DISP_OPEN_IF:
374 0 : flags = create_flags;
375 0 : break;
376 0 : default:
377 0 : flags = 0;
378 0 : break;
379 : }
380 :
381 4 : flags |= rdwr_flags;
382 :
383 4 : if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
384 0 : flags = O_RDONLY | O_DIRECTORY;
385 0 : if (readonly) {
386 0 : goto do_open;
387 : }
388 0 : switch (io->generic.in.open_disposition) {
389 0 : case NTCREATEX_DISP_CREATE:
390 0 : if (mkdir(unix_path, 0755) == -1) {
391 0 : DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
392 0 : return map_nt_error_from_unix_common(errno);
393 : }
394 0 : break;
395 0 : case NTCREATEX_DISP_OPEN_IF:
396 0 : if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
397 0 : DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
398 0 : return map_nt_error_from_unix_common(errno);
399 : }
400 0 : break;
401 : }
402 : }
403 :
404 4 : do_open:
405 4 : fd = open(unix_path, flags, 0644);
406 4 : if (fd == -1) {
407 0 : return map_nt_error_from_unix_common(errno);
408 : }
409 :
410 4 : if (fstat(fd, &st) == -1) {
411 0 : DEBUG(9,("svfs_open: fstat errno=%d\n", errno));
412 0 : close(fd);
413 0 : return map_nt_error_from_unix_common(errno);
414 : }
415 :
416 4 : status = ntvfs_handle_new(ntvfs, req, &handle);
417 4 : NT_STATUS_NOT_OK_RETURN(status);
418 :
419 4 : f = talloc(handle, struct svfs_file);
420 4 : if (f == NULL) {
421 0 : close(fd);
422 0 : return NT_STATUS_NO_MEMORY;
423 : }
424 4 : f->fd = fd;
425 4 : f->name = talloc_strdup(f, unix_path);
426 4 : NT_STATUS_HAVE_NO_MEMORY(f->name);
427 :
428 4 : DLIST_ADD(p->open_files, f);
429 :
430 4 : status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
431 4 : NT_STATUS_NOT_OK_RETURN(status);
432 :
433 4 : ZERO_STRUCT(io->generic.out);
434 :
435 4 : unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
436 4 : unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
437 4 : unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
438 4 : unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
439 4 : io->generic.out.file.ntvfs = handle;
440 4 : io->generic.out.alloc_size = st.st_size;
441 4 : io->generic.out.size = st.st_size;
442 4 : io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
443 4 : io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
444 :
445 4 : return NT_STATUS_OK;
446 : }
447 :
448 : /*
449 : create a directory
450 : */
451 0 : static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
452 : struct ntvfs_request *req, union smb_mkdir *md)
453 : {
454 0 : char *unix_path;
455 :
456 0 : CHECK_READ_ONLY(req);
457 :
458 0 : if (md->generic.level != RAW_MKDIR_MKDIR) {
459 0 : return NT_STATUS_INVALID_LEVEL;
460 : }
461 :
462 0 : unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
463 :
464 0 : if (mkdir(unix_path, 0777) == -1) {
465 0 : return map_nt_error_from_unix_common(errno);
466 : }
467 :
468 0 : return NT_STATUS_OK;
469 : }
470 :
471 : /*
472 : remove a directory
473 : */
474 0 : static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
475 : struct ntvfs_request *req, struct smb_rmdir *rd)
476 : {
477 0 : char *unix_path;
478 :
479 0 : CHECK_READ_ONLY(req);
480 :
481 0 : unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
482 :
483 0 : if (rmdir(unix_path) == -1) {
484 0 : return map_nt_error_from_unix_common(errno);
485 : }
486 :
487 0 : return NT_STATUS_OK;
488 : }
489 :
490 : /*
491 : rename a set of files
492 : */
493 0 : static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
494 : struct ntvfs_request *req, union smb_rename *ren)
495 : {
496 0 : char *unix_path1, *unix_path2;
497 :
498 0 : CHECK_READ_ONLY(req);
499 :
500 0 : if (ren->generic.level != RAW_RENAME_RENAME) {
501 0 : return NT_STATUS_INVALID_LEVEL;
502 : }
503 :
504 0 : unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
505 0 : unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
506 :
507 0 : if (rename(unix_path1, unix_path2) == -1) {
508 0 : return map_nt_error_from_unix_common(errno);
509 : }
510 :
511 0 : return NT_STATUS_OK;
512 : }
513 :
514 : /*
515 : copy a set of files
516 : */
517 0 : static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
518 : struct ntvfs_request *req, struct smb_copy *cp)
519 : {
520 0 : return NT_STATUS_NOT_SUPPORTED;
521 : }
522 :
523 : /*
524 : read from a file
525 : */
526 49 : static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
527 : struct ntvfs_request *req, union smb_read *rd)
528 : {
529 49 : struct svfs_private *p = ntvfs->private_data;
530 0 : struct svfs_file *f;
531 0 : ssize_t ret;
532 :
533 49 : if (rd->generic.level != RAW_READ_READX) {
534 0 : return NT_STATUS_NOT_SUPPORTED;
535 : }
536 :
537 49 : f = find_fd(p, rd->readx.in.file.ntvfs);
538 49 : if (!f) {
539 0 : return NT_STATUS_INVALID_HANDLE;
540 : }
541 :
542 49 : ret = pread(f->fd,
543 49 : rd->readx.out.data,
544 49 : rd->readx.in.maxcnt,
545 49 : rd->readx.in.offset);
546 49 : if (ret == -1) {
547 0 : return map_nt_error_from_unix_common(errno);
548 : }
549 :
550 49 : rd->readx.out.nread = ret;
551 49 : rd->readx.out.remaining = 0; /* should fill this in? */
552 49 : rd->readx.out.compaction_mode = 0;
553 :
554 49 : return NT_STATUS_OK;
555 : }
556 :
557 : /*
558 : write to a file
559 : */
560 49 : static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
561 : struct ntvfs_request *req, union smb_write *wr)
562 : {
563 49 : struct svfs_private *p = ntvfs->private_data;
564 0 : struct svfs_file *f;
565 0 : ssize_t ret;
566 :
567 49 : if (wr->generic.level != RAW_WRITE_WRITEX) {
568 0 : return ntvfs_map_write(ntvfs, req, wr);
569 : }
570 :
571 49 : CHECK_READ_ONLY(req);
572 :
573 49 : f = find_fd(p, wr->writex.in.file.ntvfs);
574 49 : if (!f) {
575 0 : return NT_STATUS_INVALID_HANDLE;
576 : }
577 :
578 49 : ret = pwrite(f->fd,
579 49 : wr->writex.in.data,
580 49 : wr->writex.in.count,
581 49 : wr->writex.in.offset);
582 49 : if (ret == -1) {
583 0 : return map_nt_error_from_unix_common(errno);
584 : }
585 :
586 49 : wr->writex.out.nwritten = ret;
587 49 : wr->writex.out.remaining = 0; /* should fill this in? */
588 :
589 49 : return NT_STATUS_OK;
590 : }
591 :
592 : /*
593 : seek in a file
594 : */
595 0 : static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
596 : struct ntvfs_request *req,
597 : union smb_seek *io)
598 : {
599 0 : return NT_STATUS_NOT_SUPPORTED;
600 : }
601 :
602 : /*
603 : flush a file
604 : */
605 0 : static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
606 : struct ntvfs_request *req,
607 : union smb_flush *io)
608 : {
609 0 : struct svfs_private *p = ntvfs->private_data;
610 0 : struct svfs_file *f;
611 :
612 0 : switch (io->generic.level) {
613 0 : case RAW_FLUSH_FLUSH:
614 : case RAW_FLUSH_SMB2:
615 : /* ignore the additional unknown option in SMB2 */
616 0 : f = find_fd(p, io->generic.in.file.ntvfs);
617 0 : if (!f) {
618 0 : return NT_STATUS_INVALID_HANDLE;
619 : }
620 0 : fsync(f->fd);
621 0 : return NT_STATUS_OK;
622 :
623 0 : case RAW_FLUSH_ALL:
624 0 : for (f=p->open_files;f;f=f->next) {
625 0 : fsync(f->fd);
626 : }
627 0 : return NT_STATUS_OK;
628 : }
629 :
630 0 : return NT_STATUS_INVALID_LEVEL;
631 : }
632 :
633 : /*
634 : close a file
635 : */
636 4 : static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
637 : struct ntvfs_request *req,
638 : union smb_close *io)
639 : {
640 4 : struct svfs_private *p = ntvfs->private_data;
641 0 : struct svfs_file *f;
642 :
643 4 : if (io->generic.level != RAW_CLOSE_CLOSE) {
644 : /* we need a mapping function */
645 0 : return NT_STATUS_INVALID_LEVEL;
646 : }
647 :
648 4 : f = find_fd(p, io->close.in.file.ntvfs);
649 4 : if (!f) {
650 0 : return NT_STATUS_INVALID_HANDLE;
651 : }
652 :
653 4 : if (close(f->fd) == -1) {
654 0 : return map_nt_error_from_unix_common(errno);
655 : }
656 :
657 4 : DLIST_REMOVE(p->open_files, f);
658 4 : talloc_free(f->name);
659 4 : talloc_free(f);
660 :
661 4 : return NT_STATUS_OK;
662 : }
663 :
664 : /*
665 : exit - closing files
666 : */
667 0 : static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
668 : struct ntvfs_request *req)
669 : {
670 0 : return NT_STATUS_NOT_SUPPORTED;
671 : }
672 :
673 : /*
674 : logoff - closing files
675 : */
676 0 : static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
677 : struct ntvfs_request *req)
678 : {
679 0 : return NT_STATUS_NOT_SUPPORTED;
680 : }
681 :
682 : /*
683 : setup for an async call
684 : */
685 0 : static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
686 : struct ntvfs_request *req,
687 : void *private_data)
688 : {
689 0 : return NT_STATUS_OK;
690 : }
691 :
692 : /*
693 : cancel an async call
694 : */
695 0 : static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
696 : {
697 0 : return NT_STATUS_UNSUCCESSFUL;
698 : }
699 :
700 : /*
701 : lock a byte range
702 : */
703 0 : static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
704 : struct ntvfs_request *req, union smb_lock *lck)
705 : {
706 0 : DEBUG(0,("REWRITE: not doing byte range locking!\n"));
707 0 : return NT_STATUS_OK;
708 : }
709 :
710 : /*
711 : set info on a pathname
712 : */
713 0 : static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
714 : struct ntvfs_request *req, union smb_setfileinfo *st)
715 : {
716 0 : CHECK_READ_ONLY(req);
717 :
718 0 : return NT_STATUS_NOT_SUPPORTED;
719 : }
720 :
721 : /*
722 : set info on a open file
723 : */
724 0 : static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
725 : struct ntvfs_request *req,
726 : union smb_setfileinfo *info)
727 : {
728 0 : struct svfs_private *p = ntvfs->private_data;
729 0 : struct svfs_file *f;
730 0 : struct utimbuf unix_times;
731 :
732 0 : CHECK_READ_ONLY(req);
733 :
734 0 : f = find_fd(p, info->generic.in.file.ntvfs);
735 0 : if (!f) {
736 0 : return NT_STATUS_INVALID_HANDLE;
737 : }
738 :
739 0 : switch (info->generic.level) {
740 0 : case RAW_SFILEINFO_END_OF_FILE_INFO:
741 : case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
742 0 : if (ftruncate(f->fd,
743 0 : info->end_of_file_info.in.size) == -1) {
744 0 : return map_nt_error_from_unix_common(errno);
745 : }
746 0 : break;
747 0 : case RAW_SFILEINFO_SETATTRE:
748 0 : unix_times.actime = info->setattre.in.access_time;
749 0 : unix_times.modtime = info->setattre.in.write_time;
750 :
751 0 : if (unix_times.actime == 0 && unix_times.modtime == 0) {
752 0 : break;
753 : }
754 :
755 : /* set modify time = to access time if modify time was 0 */
756 0 : if (unix_times.actime != 0 && unix_times.modtime == 0) {
757 0 : unix_times.modtime = unix_times.actime;
758 : }
759 :
760 : /* Set the date on this file */
761 0 : if (svfs_file_utime(f->fd, &unix_times) != 0) {
762 0 : return NT_STATUS_ACCESS_DENIED;
763 : }
764 0 : break;
765 0 : default:
766 0 : DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
767 : info->generic.level));
768 0 : return NT_STATUS_NOT_IMPLEMENTED;
769 : }
770 0 : return NT_STATUS_OK;
771 : }
772 :
773 :
774 : /*
775 : return filesystem space info
776 : */
777 0 : static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
778 : struct ntvfs_request *req, union smb_fsinfo *fs)
779 : {
780 0 : struct svfs_private *p = ntvfs->private_data;
781 0 : struct stat st;
782 :
783 0 : if (fs->generic.level != RAW_QFS_GENERIC) {
784 0 : return ntvfs_map_fsinfo(ntvfs, req, fs);
785 : }
786 :
787 0 : if (sys_fsusage(p->connectpath,
788 : &fs->generic.out.blocks_free,
789 : &fs->generic.out.blocks_total) == -1) {
790 0 : return map_nt_error_from_unix_common(errno);
791 : }
792 :
793 0 : fs->generic.out.block_size = 512;
794 :
795 0 : if (stat(p->connectpath, &st) != 0) {
796 0 : return NT_STATUS_DISK_CORRUPT_ERROR;
797 : }
798 :
799 0 : fs->generic.out.fs_id = st.st_ino;
800 0 : unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
801 0 : fs->generic.out.serial_number = st.st_ino;
802 0 : fs->generic.out.fs_attr = 0;
803 0 : fs->generic.out.max_file_component_length = 255;
804 0 : fs->generic.out.device_type = 0;
805 0 : fs->generic.out.device_characteristics = 0;
806 0 : fs->generic.out.quota_soft = 0;
807 0 : fs->generic.out.quota_hard = 0;
808 0 : fs->generic.out.quota_flags = 0;
809 0 : fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
810 0 : fs->generic.out.fs_type = ntvfs->ctx->fs_type;
811 :
812 0 : return NT_STATUS_OK;
813 : }
814 :
815 : #if 0
816 : /*
817 : return filesystem attribute info
818 : */
819 : static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
820 : struct ntvfs_request *req, union smb_fsattr *fs)
821 : {
822 : struct stat st;
823 : struct svfs_private *p = ntvfs->private_data;
824 :
825 : if (fs->generic.level != RAW_FSATTR_GENERIC) {
826 : return ntvfs_map_fsattr(ntvfs, req, fs);
827 : }
828 :
829 : if (stat(p->connectpath, &st) == -1) {
830 : return map_nt_error_from_unix_common(errno);
831 : }
832 :
833 : unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
834 : fs->generic.out.fs_attr =
835 : FILE_CASE_PRESERVED_NAMES |
836 : FILE_CASE_SENSITIVE_SEARCH |
837 : FILE_PERSISTENT_ACLS;
838 : fs->generic.out.max_file_component_length = 255;
839 : fs->generic.out.serial_number = 1;
840 : fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
841 : fs->generic.out.volume_name = talloc_strdup(req,
842 : lpcfg_servicename(req->tcon->service));
843 :
844 : return NT_STATUS_OK;
845 : }
846 : #endif
847 :
848 : /*
849 : return print queue info
850 : */
851 0 : static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
852 : struct ntvfs_request *req, union smb_lpq *lpq)
853 : {
854 0 : return NT_STATUS_NOT_SUPPORTED;
855 : }
856 :
857 : /*
858 : list files in a directory matching a wildcard pattern
859 : */
860 0 : static NTSTATUS svfs_search_first(struct ntvfs_module_context *ntvfs,
861 : struct ntvfs_request *req, union smb_search_first *io,
862 : void *search_private,
863 : bool (*callback)(void *, const union smb_search_data *))
864 : {
865 0 : struct svfs_dir *dir;
866 0 : int i;
867 0 : struct svfs_private *p = ntvfs->private_data;
868 0 : struct search_state *search;
869 0 : union smb_search_data file;
870 0 : unsigned int max_count;
871 :
872 0 : if (io->generic.level != RAW_SEARCH_TRANS2) {
873 0 : return NT_STATUS_NOT_SUPPORTED;
874 : }
875 :
876 0 : if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
877 0 : return NT_STATUS_NOT_SUPPORTED;
878 : }
879 :
880 0 : search = talloc_zero(p, struct search_state);
881 0 : if (!search) {
882 0 : return NT_STATUS_NO_MEMORY;
883 : }
884 :
885 0 : max_count = io->t2ffirst.in.max_count;
886 :
887 0 : dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
888 0 : if (!dir) {
889 0 : return NT_STATUS_FOOBAR;
890 : }
891 :
892 0 : search->handle = p->next_search_handle;
893 0 : search->dir = dir;
894 :
895 0 : if (dir->count < max_count) {
896 0 : max_count = dir->count;
897 : }
898 :
899 0 : for (i=0; i < max_count;i++) {
900 0 : ZERO_STRUCT(file);
901 0 : unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
902 0 : unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
903 0 : unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
904 0 : unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
905 0 : file.both_directory_info.name.s = dir->files[i].name;
906 0 : file.both_directory_info.short_name.s = dir->files[i].name;
907 0 : file.both_directory_info.size = dir->files[i].st.st_size;
908 0 : file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
909 :
910 0 : if (!callback(search_private, &file)) {
911 0 : break;
912 : }
913 : }
914 :
915 0 : search->current_index = i;
916 :
917 0 : io->t2ffirst.out.count = i;
918 0 : io->t2ffirst.out.handle = search->handle;
919 0 : io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
920 :
921 : /* work out if we are going to keep the search state */
922 0 : if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
923 0 : ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
924 0 : talloc_free(search);
925 : } else {
926 0 : p->next_search_handle++;
927 0 : DLIST_ADD(p->search, search);
928 : }
929 :
930 0 : return NT_STATUS_OK;
931 : }
932 :
933 : /* continue a search */
934 0 : static NTSTATUS svfs_search_next(struct ntvfs_module_context *ntvfs,
935 : struct ntvfs_request *req, union smb_search_next *io,
936 : void *search_private,
937 : bool (*callback)(void *, const union smb_search_data *))
938 : {
939 0 : struct svfs_dir *dir;
940 0 : int i;
941 0 : struct svfs_private *p = ntvfs->private_data;
942 0 : struct search_state *search;
943 0 : union smb_search_data file;
944 0 : unsigned int max_count;
945 :
946 0 : if (io->generic.level != RAW_SEARCH_TRANS2) {
947 0 : return NT_STATUS_NOT_SUPPORTED;
948 : }
949 :
950 0 : if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
951 0 : return NT_STATUS_NOT_SUPPORTED;
952 : }
953 :
954 0 : for (search=p->search; search; search = search->next) {
955 0 : if (search->handle == io->t2fnext.in.handle) break;
956 : }
957 :
958 0 : if (!search) {
959 : /* we didn't find the search handle */
960 0 : return NT_STATUS_FOOBAR;
961 : }
962 :
963 0 : dir = search->dir;
964 :
965 : /* the client might be asking for something other than just continuing
966 : with the search */
967 0 : if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
968 0 : (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
969 0 : io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
970 : /* look backwards first */
971 0 : for (i=search->current_index; i > 0; i--) {
972 0 : if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
973 0 : search->current_index = i;
974 0 : goto found;
975 : }
976 : }
977 :
978 : /* then look forwards */
979 0 : for (i=search->current_index+1; i <= dir->count; i++) {
980 0 : if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
981 0 : search->current_index = i;
982 0 : goto found;
983 : }
984 : }
985 : }
986 :
987 0 : found:
988 0 : max_count = search->current_index + io->t2fnext.in.max_count;
989 :
990 0 : if (max_count > dir->count) {
991 0 : max_count = dir->count;
992 : }
993 :
994 0 : for (i = search->current_index; i < max_count;i++) {
995 0 : ZERO_STRUCT(file);
996 0 : unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
997 0 : unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
998 0 : unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
999 0 : unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
1000 0 : file.both_directory_info.name.s = dir->files[i].name;
1001 0 : file.both_directory_info.short_name.s = dir->files[i].name;
1002 0 : file.both_directory_info.size = dir->files[i].st.st_size;
1003 0 : file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
1004 :
1005 0 : if (!callback(search_private, &file)) {
1006 0 : break;
1007 : }
1008 : }
1009 :
1010 0 : io->t2fnext.out.count = i - search->current_index;
1011 0 : io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
1012 :
1013 0 : search->current_index = i;
1014 :
1015 : /* work out if we are going to keep the search state */
1016 0 : if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
1017 0 : ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
1018 0 : DLIST_REMOVE(p->search, search);
1019 0 : talloc_free(search);
1020 : }
1021 :
1022 0 : return NT_STATUS_OK;
1023 : }
1024 :
1025 : /* close a search */
1026 0 : static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
1027 : struct ntvfs_request *req, union smb_search_close *io)
1028 : {
1029 0 : struct svfs_private *p = ntvfs->private_data;
1030 0 : struct search_state *search;
1031 :
1032 0 : for (search=p->search; search; search = search->next) {
1033 0 : if (search->handle == io->findclose.in.handle) break;
1034 : }
1035 :
1036 0 : if (!search) {
1037 : /* we didn't find the search handle */
1038 0 : return NT_STATUS_FOOBAR;
1039 : }
1040 :
1041 0 : DLIST_REMOVE(p->search, search);
1042 0 : talloc_free(search);
1043 :
1044 0 : return NT_STATUS_OK;
1045 : }
1046 :
1047 : /* SMBtrans - not used on file shares */
1048 0 : static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
1049 : struct ntvfs_request *req, struct smb_trans2 *trans2)
1050 : {
1051 0 : return NT_STATUS_ACCESS_DENIED;
1052 : }
1053 :
1054 :
1055 : /*
1056 : initialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
1057 : */
1058 68 : NTSTATUS ntvfs_simple_init(TALLOC_CTX *ctx)
1059 : {
1060 3 : NTSTATUS ret;
1061 3 : struct ntvfs_ops ops;
1062 68 : NTVFS_CURRENT_CRITICAL_SIZES(vers);
1063 :
1064 68 : ZERO_STRUCT(ops);
1065 :
1066 : /* fill in all the operations */
1067 68 : ops.connect_fn = svfs_connect;
1068 68 : ops.disconnect_fn = svfs_disconnect;
1069 68 : ops.unlink_fn = svfs_unlink;
1070 68 : ops.chkpath_fn = svfs_chkpath;
1071 68 : ops.qpathinfo_fn = svfs_qpathinfo;
1072 68 : ops.setpathinfo_fn = svfs_setpathinfo;
1073 68 : ops.open_fn = svfs_open;
1074 68 : ops.mkdir_fn = svfs_mkdir;
1075 68 : ops.rmdir_fn = svfs_rmdir;
1076 68 : ops.rename_fn = svfs_rename;
1077 68 : ops.copy_fn = svfs_copy;
1078 68 : ops.ioctl_fn = svfs_ioctl;
1079 68 : ops.read_fn = svfs_read;
1080 68 : ops.write_fn = svfs_write;
1081 68 : ops.seek_fn = svfs_seek;
1082 68 : ops.flush_fn = svfs_flush;
1083 68 : ops.close_fn = svfs_close;
1084 68 : ops.exit_fn = svfs_exit;
1085 68 : ops.lock_fn = svfs_lock;
1086 68 : ops.setfileinfo_fn = svfs_setfileinfo;
1087 68 : ops.qfileinfo_fn = svfs_qfileinfo;
1088 68 : ops.fsinfo_fn = svfs_fsinfo;
1089 68 : ops.lpq_fn = svfs_lpq;
1090 68 : ops.search_first_fn = svfs_search_first;
1091 68 : ops.search_next_fn = svfs_search_next;
1092 68 : ops.search_close_fn = svfs_search_close;
1093 68 : ops.trans_fn = svfs_trans;
1094 68 : ops.logoff_fn = svfs_logoff;
1095 68 : ops.async_setup_fn = svfs_async_setup;
1096 68 : ops.cancel_fn = svfs_cancel;
1097 :
1098 : /* register ourselves with the NTVFS subsystem. We register
1099 : under names 'simple'
1100 : */
1101 :
1102 68 : ops.type = NTVFS_DISK;
1103 68 : ops.name = "simple";
1104 68 : ret = ntvfs_register(&ops, &vers);
1105 :
1106 68 : if (!NT_STATUS_IS_OK(ret)) {
1107 0 : DEBUG(0,("Failed to register simple backend with name: %s!\n",
1108 : ops.name));
1109 : }
1110 :
1111 68 : return ret;
1112 : }
|