s3:smb2_server: use smbd_smb2_request_verify_sizes() in smb2_read.c
[Samba.git] / source4 / ntvfs / simple / vfs_simple.c
blob03bf76634d14412e3d46a0d15f936ab5c0e5e952
1 /*
2 Unix SMB/CIFS implementation.
4 simple NTVFS filesystem backend
6 Copyright (C) Andrew Tridgell 2003
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.
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.
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/>.
22 this implements a very simple NTVFS filesystem backend.
24 this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
25 minimal work to give a working backend.
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"
37 #ifndef O_DIRECTORY
38 #define O_DIRECTORY 0
39 #endif
41 #define CHECK_READ_ONLY(req) do { if (share_bool_option(ntvfs->ctx->config, SHARE_READONLY, true)) return NT_STATUS_ACCESS_DENIED; } while (0)
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)
49 static NTSTATUS svfs_connect(struct ntvfs_module_context *ntvfs,
50 struct ntvfs_request *req,
51 union smb_tcon* tcon)
53 struct stat st;
54 struct svfs_private *p;
55 struct share_config *scfg = ntvfs->ctx->config;
56 const char *sharename;
58 switch (tcon->generic.level) {
59 case RAW_TCON_TCON:
60 sharename = tcon->tcon.in.service;
61 break;
62 case RAW_TCON_TCONX:
63 sharename = tcon->tconx.in.path;
64 break;
65 case RAW_TCON_SMB2:
66 sharename = tcon->smb2.in.path;
67 break;
68 default:
69 return NT_STATUS_INVALID_LEVEL;
72 if (strncmp(sharename, "\\\\", 2) == 0) {
73 char *p2 = strchr(sharename+2, '\\');
74 if (p2) {
75 sharename = p2 + 1;
79 p = talloc(ntvfs, struct svfs_private);
80 NT_STATUS_HAVE_NO_MEMORY(p);
81 p->ntvfs = ntvfs;
82 p->next_search_handle = 0;
83 p->connectpath = talloc_strdup(p, share_string_option(scfg, SHARE_PATH, ""));
84 p->open_files = NULL;
85 p->search = NULL;
87 /* the directory must exist */
88 if (stat(p->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
89 DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n",
90 p->connectpath, sharename));
91 return NT_STATUS_BAD_NETWORK_NAME;
94 ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
95 NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
96 ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
97 NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
99 if (tcon->generic.level == RAW_TCON_TCONX) {
100 tcon->tconx.out.fs_type = ntvfs->ctx->fs_type;
101 tcon->tconx.out.dev_type = ntvfs->ctx->dev_type;
104 ntvfs->private_data = p;
106 return NT_STATUS_OK;
110 disconnect from a share
112 static NTSTATUS svfs_disconnect(struct ntvfs_module_context *ntvfs)
114 return NT_STATUS_OK;
118 find open file handle given fd
120 static struct svfs_file *find_fd(struct svfs_private *sp, struct ntvfs_handle *handle)
122 struct svfs_file *f;
123 void *p;
125 p = ntvfs_handle_get_backend_data(handle, sp->ntvfs);
126 if (!p) return NULL;
128 f = talloc_get_type(p, struct svfs_file);
129 if (!f) return NULL;
131 return f;
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)
138 static NTSTATUS svfs_unlink(struct ntvfs_module_context *ntvfs,
139 struct ntvfs_request *req,
140 union smb_unlink *unl)
142 char *unix_path;
144 CHECK_READ_ONLY(req);
146 unix_path = svfs_unix_path(ntvfs, req, unl->unlink.in.pattern);
148 /* ignoring wildcards ... */
149 if (unlink(unix_path) == -1) {
150 return map_nt_error_from_unix(errno);
153 return NT_STATUS_OK;
158 ioctl interface - we don't do any
160 static NTSTATUS svfs_ioctl(struct ntvfs_module_context *ntvfs,
161 struct ntvfs_request *req, union smb_ioctl *io)
163 return NT_STATUS_INVALID_PARAMETER;
167 check if a directory exists
169 static NTSTATUS svfs_chkpath(struct ntvfs_module_context *ntvfs,
170 struct ntvfs_request *req,
171 union smb_chkpath *cp)
173 char *unix_path;
174 struct stat st;
176 unix_path = svfs_unix_path(ntvfs, req, cp->chkpath.in.path);
178 if (stat(unix_path, &st) == -1) {
179 return map_nt_error_from_unix(errno);
182 if (!S_ISDIR(st.st_mode)) {
183 return NT_STATUS_NOT_A_DIRECTORY;
186 return NT_STATUS_OK;
190 build a file_id from a stat struct
192 static uint64_t svfs_file_id(struct stat *st)
194 uint64_t ret = st->st_ino;
195 ret <<= 32;
196 ret |= st->st_dev;
197 return ret;
201 approximately map a struct stat to a generic fileinfo struct
203 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)
207 struct svfs_dir *dir = NULL;
208 char *pattern = NULL;
209 int i;
210 const char *s, *short_name;
212 s = strrchr(unix_path, '/');
213 if (s) {
214 short_name = s+1;
215 } else {
216 short_name = "";
219 asprintf(&pattern, "%s:*", unix_path);
221 if (pattern) {
222 dir = svfs_list_unix(req, req, pattern);
225 unix_to_nt_time(&info->generic.out.create_time, st->st_ctime);
226 unix_to_nt_time(&info->generic.out.access_time, st->st_atime);
227 unix_to_nt_time(&info->generic.out.write_time, st->st_mtime);
228 unix_to_nt_time(&info->generic.out.change_time, st->st_mtime);
229 info->generic.out.alloc_size = st->st_size;
230 info->generic.out.size = st->st_size;
231 info->generic.out.attrib = svfs_unix_to_dos_attrib(st->st_mode);
232 info->generic.out.alloc_size = st->st_blksize * st->st_blocks;
233 info->generic.out.nlink = st->st_nlink;
234 info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
235 info->generic.out.file_id = svfs_file_id(st);
236 /* REWRITE: TODO stuff in here */
237 info->generic.out.delete_pending = 0;
238 info->generic.out.ea_size = 0;
239 info->generic.out.num_eas = 0;
240 info->generic.out.fname.s = talloc_strdup(req, short_name);
241 info->generic.out.alt_fname.s = talloc_strdup(req, short_name);
242 info->generic.out.compressed_size = 0;
243 info->generic.out.format = 0;
244 info->generic.out.unit_shift = 0;
245 info->generic.out.chunk_shift = 0;
246 info->generic.out.cluster_shift = 0;
248 info->generic.out.access_flags = 0;
249 info->generic.out.position = 0;
250 info->generic.out.mode = 0;
251 info->generic.out.alignment_requirement = 0;
252 info->generic.out.reparse_tag = 0;
253 info->generic.out.num_streams = 0;
254 /* setup a single data stream */
255 info->generic.out.num_streams = 1 + (dir?dir->count:0);
256 info->generic.out.streams = talloc_array(req,
257 struct stream_struct,
258 info->generic.out.num_streams);
259 if (!info->generic.out.streams) {
260 return NT_STATUS_NO_MEMORY;
262 info->generic.out.streams[0].size = st->st_size;
263 info->generic.out.streams[0].alloc_size = st->st_size;
264 info->generic.out.streams[0].stream_name.s = talloc_strdup(req,"::$DATA");
266 for (i=0;dir && i<dir->count;i++) {
267 s = strchr(dir->files[i].name, ':');
268 info->generic.out.streams[1+i].size = dir->files[i].st.st_size;
269 info->generic.out.streams[1+i].alloc_size = dir->files[i].st.st_size;
270 info->generic.out.streams[1+i].stream_name.s = s?s:dir->files[i].name;
273 return NT_STATUS_OK;
277 return info on a pathname
279 static NTSTATUS svfs_qpathinfo(struct ntvfs_module_context *ntvfs,
280 struct ntvfs_request *req, union smb_fileinfo *info)
282 char *unix_path;
283 struct stat st;
285 DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.file.path, info->generic.level));
286 if (info->generic.level != RAW_FILEINFO_GENERIC) {
287 return ntvfs_map_qpathinfo(ntvfs, req, info);
290 unix_path = svfs_unix_path(ntvfs, req, info->generic.in.file.path);
291 DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path));
292 if (stat(unix_path, &st) == -1) {
293 DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno));
294 return map_nt_error_from_unix(errno);
296 DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path));
297 return svfs_map_fileinfo(ntvfs, req, info, &st, unix_path);
301 query info on a open file
303 static NTSTATUS svfs_qfileinfo(struct ntvfs_module_context *ntvfs,
304 struct ntvfs_request *req, union smb_fileinfo *info)
306 struct svfs_private *p = ntvfs->private_data;
307 struct svfs_file *f;
308 struct stat st;
310 if (info->generic.level != RAW_FILEINFO_GENERIC) {
311 return ntvfs_map_qfileinfo(ntvfs, req, info);
314 f = find_fd(p, info->generic.in.file.ntvfs);
315 if (!f) {
316 return NT_STATUS_INVALID_HANDLE;
319 if (fstat(f->fd, &st) == -1) {
320 return map_nt_error_from_unix(errno);
323 return svfs_map_fileinfo(ntvfs, req,info, &st, f->name);
328 open a file
330 static NTSTATUS svfs_open(struct ntvfs_module_context *ntvfs,
331 struct ntvfs_request *req, union smb_open *io)
333 struct svfs_private *p = ntvfs->private_data;
334 char *unix_path;
335 struct stat st;
336 int fd, flags;
337 struct svfs_file *f;
338 int create_flags, rdwr_flags;
339 bool readonly;
340 NTSTATUS status;
341 struct ntvfs_handle *handle;
343 if (io->generic.level != RAW_OPEN_GENERIC) {
344 return ntvfs_map_open(ntvfs, req, io);
347 readonly = share_bool_option(ntvfs->ctx->config, SHARE_READONLY, SHARE_READONLY_DEFAULT);
348 if (readonly) {
349 create_flags = 0;
350 rdwr_flags = O_RDONLY;
351 } else {
352 create_flags = O_CREAT;
353 rdwr_flags = O_RDWR;
356 unix_path = svfs_unix_path(ntvfs, req, io->ntcreatex.in.fname);
358 switch (io->generic.in.open_disposition) {
359 case NTCREATEX_DISP_SUPERSEDE:
360 case NTCREATEX_DISP_OVERWRITE_IF:
361 flags = create_flags | O_TRUNC;
362 break;
363 case NTCREATEX_DISP_OPEN:
364 case NTCREATEX_DISP_OVERWRITE:
365 flags = 0;
366 break;
367 case NTCREATEX_DISP_CREATE:
368 flags = create_flags | O_EXCL;
369 break;
370 case NTCREATEX_DISP_OPEN_IF:
371 flags = create_flags;
372 break;
373 default:
374 flags = 0;
375 break;
378 flags |= rdwr_flags;
380 if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
381 flags = O_RDONLY | O_DIRECTORY;
382 if (readonly) {
383 goto do_open;
385 switch (io->generic.in.open_disposition) {
386 case NTCREATEX_DISP_CREATE:
387 if (mkdir(unix_path, 0755) == -1) {
388 DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
389 return map_nt_error_from_unix(errno);
391 break;
392 case NTCREATEX_DISP_OPEN_IF:
393 if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
394 DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
395 return map_nt_error_from_unix(errno);
397 break;
401 do_open:
402 fd = open(unix_path, flags, 0644);
403 if (fd == -1) {
404 return map_nt_error_from_unix(errno);
407 if (fstat(fd, &st) == -1) {
408 DEBUG(9,("svfs_open: fstat errno=%d\n", errno));
409 close(fd);
410 return map_nt_error_from_unix(errno);
413 status = ntvfs_handle_new(ntvfs, req, &handle);
414 NT_STATUS_NOT_OK_RETURN(status);
416 f = talloc(handle, struct svfs_file);
417 NT_STATUS_HAVE_NO_MEMORY(f);
418 f->fd = fd;
419 f->name = talloc_strdup(f, unix_path);
420 NT_STATUS_HAVE_NO_MEMORY(f->name);
422 DLIST_ADD(p->open_files, f);
424 status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
425 NT_STATUS_NOT_OK_RETURN(status);
427 ZERO_STRUCT(io->generic.out);
429 unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
430 unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
431 unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
432 unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
433 io->generic.out.file.ntvfs = handle;
434 io->generic.out.alloc_size = st.st_size;
435 io->generic.out.size = st.st_size;
436 io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
437 io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
439 return NT_STATUS_OK;
443 create a directory
445 static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
446 struct ntvfs_request *req, union smb_mkdir *md)
448 char *unix_path;
450 CHECK_READ_ONLY(req);
452 if (md->generic.level != RAW_MKDIR_MKDIR) {
453 return NT_STATUS_INVALID_LEVEL;
456 unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
458 if (mkdir(unix_path, 0777) == -1) {
459 return map_nt_error_from_unix(errno);
462 return NT_STATUS_OK;
466 remove a directory
468 static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
469 struct ntvfs_request *req, struct smb_rmdir *rd)
471 char *unix_path;
473 CHECK_READ_ONLY(req);
475 unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
477 if (rmdir(unix_path) == -1) {
478 return map_nt_error_from_unix(errno);
481 return NT_STATUS_OK;
485 rename a set of files
487 static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
488 struct ntvfs_request *req, union smb_rename *ren)
490 char *unix_path1, *unix_path2;
492 CHECK_READ_ONLY(req);
494 if (ren->generic.level != RAW_RENAME_RENAME) {
495 return NT_STATUS_INVALID_LEVEL;
498 unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
499 unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
501 if (rename(unix_path1, unix_path2) == -1) {
502 return map_nt_error_from_unix(errno);
505 return NT_STATUS_OK;
509 copy a set of files
511 static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
512 struct ntvfs_request *req, struct smb_copy *cp)
514 return NT_STATUS_NOT_SUPPORTED;
518 read from a file
520 static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
521 struct ntvfs_request *req, union smb_read *rd)
523 struct svfs_private *p = ntvfs->private_data;
524 struct svfs_file *f;
525 ssize_t ret;
527 if (rd->generic.level != RAW_READ_READX) {
528 return NT_STATUS_NOT_SUPPORTED;
531 f = find_fd(p, rd->readx.in.file.ntvfs);
532 if (!f) {
533 return NT_STATUS_INVALID_HANDLE;
536 ret = pread(f->fd,
537 rd->readx.out.data,
538 rd->readx.in.maxcnt,
539 rd->readx.in.offset);
540 if (ret == -1) {
541 return map_nt_error_from_unix(errno);
544 rd->readx.out.nread = ret;
545 rd->readx.out.remaining = 0; /* should fill this in? */
546 rd->readx.out.compaction_mode = 0;
548 return NT_STATUS_OK;
552 write to a file
554 static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
555 struct ntvfs_request *req, union smb_write *wr)
557 struct svfs_private *p = ntvfs->private_data;
558 struct svfs_file *f;
559 ssize_t ret;
561 if (wr->generic.level != RAW_WRITE_WRITEX) {
562 return ntvfs_map_write(ntvfs, req, wr);
565 CHECK_READ_ONLY(req);
567 f = find_fd(p, wr->writex.in.file.ntvfs);
568 if (!f) {
569 return NT_STATUS_INVALID_HANDLE;
572 ret = pwrite(f->fd,
573 wr->writex.in.data,
574 wr->writex.in.count,
575 wr->writex.in.offset);
576 if (ret == -1) {
577 return map_nt_error_from_unix(errno);
580 wr->writex.out.nwritten = ret;
581 wr->writex.out.remaining = 0; /* should fill this in? */
583 return NT_STATUS_OK;
587 seek in a file
589 static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
590 struct ntvfs_request *req,
591 union smb_seek *io)
593 return NT_STATUS_NOT_SUPPORTED;
597 flush a file
599 static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
600 struct ntvfs_request *req,
601 union smb_flush *io)
603 struct svfs_private *p = ntvfs->private_data;
604 struct svfs_file *f;
606 switch (io->generic.level) {
607 case RAW_FLUSH_FLUSH:
608 case RAW_FLUSH_SMB2:
609 /* ignore the additional unknown option in SMB2 */
610 f = find_fd(p, io->generic.in.file.ntvfs);
611 if (!f) {
612 return NT_STATUS_INVALID_HANDLE;
614 fsync(f->fd);
615 return NT_STATUS_OK;
617 case RAW_FLUSH_ALL:
618 for (f=p->open_files;f;f=f->next) {
619 fsync(f->fd);
621 return NT_STATUS_OK;
624 return NT_STATUS_INVALID_LEVEL;
628 close a file
630 static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
631 struct ntvfs_request *req,
632 union smb_close *io)
634 struct svfs_private *p = ntvfs->private_data;
635 struct svfs_file *f;
637 if (io->generic.level != RAW_CLOSE_CLOSE) {
638 /* we need a mapping function */
639 return NT_STATUS_INVALID_LEVEL;
642 f = find_fd(p, io->close.in.file.ntvfs);
643 if (!f) {
644 return NT_STATUS_INVALID_HANDLE;
647 if (close(f->fd) == -1) {
648 return map_nt_error_from_unix(errno);
651 DLIST_REMOVE(p->open_files, f);
652 talloc_free(f->name);
653 talloc_free(f);
655 return NT_STATUS_OK;
659 exit - closing files
661 static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
662 struct ntvfs_request *req)
664 return NT_STATUS_NOT_SUPPORTED;
668 logoff - closing files
670 static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
671 struct ntvfs_request *req)
673 return NT_STATUS_NOT_SUPPORTED;
677 setup for an async call
679 static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
680 struct ntvfs_request *req,
681 void *private_data)
683 return NT_STATUS_OK;
687 cancel an async call
689 static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
691 return NT_STATUS_UNSUCCESSFUL;
695 lock a byte range
697 static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
698 struct ntvfs_request *req, union smb_lock *lck)
700 DEBUG(0,("REWRITE: not doing byte range locking!\n"));
701 return NT_STATUS_OK;
705 set info on a pathname
707 static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
708 struct ntvfs_request *req, union smb_setfileinfo *st)
710 CHECK_READ_ONLY(req);
712 return NT_STATUS_NOT_SUPPORTED;
716 set info on a open file
718 static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
719 struct ntvfs_request *req,
720 union smb_setfileinfo *info)
722 struct svfs_private *p = ntvfs->private_data;
723 struct svfs_file *f;
724 struct utimbuf unix_times;
726 CHECK_READ_ONLY(req);
728 f = find_fd(p, info->generic.in.file.ntvfs);
729 if (!f) {
730 return NT_STATUS_INVALID_HANDLE;
733 switch (info->generic.level) {
734 case RAW_SFILEINFO_END_OF_FILE_INFO:
735 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
736 if (ftruncate(f->fd,
737 info->end_of_file_info.in.size) == -1) {
738 return map_nt_error_from_unix(errno);
740 break;
741 case RAW_SFILEINFO_SETATTRE:
742 unix_times.actime = info->setattre.in.access_time;
743 unix_times.modtime = info->setattre.in.write_time;
745 if (unix_times.actime == 0 && unix_times.modtime == 0) {
746 break;
749 /* set modify time = to access time if modify time was 0 */
750 if (unix_times.actime != 0 && unix_times.modtime == 0) {
751 unix_times.modtime = unix_times.actime;
754 /* Set the date on this file */
755 if (svfs_file_utime(f->fd, &unix_times) != 0) {
756 return NT_STATUS_ACCESS_DENIED;
758 break;
759 default:
760 DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
761 info->generic.level));
762 return NT_STATUS_NOT_IMPLEMENTED;
764 return NT_STATUS_OK;
769 return filesystem space info
771 static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
772 struct ntvfs_request *req, union smb_fsinfo *fs)
774 struct svfs_private *p = ntvfs->private_data;
775 struct stat st;
777 if (fs->generic.level != RAW_QFS_GENERIC) {
778 return ntvfs_map_fsinfo(ntvfs, req, fs);
781 if (sys_fsusage(p->connectpath,
782 &fs->generic.out.blocks_free,
783 &fs->generic.out.blocks_total) == -1) {
784 return map_nt_error_from_unix(errno);
787 fs->generic.out.block_size = 512;
789 if (stat(p->connectpath, &st) != 0) {
790 return NT_STATUS_DISK_CORRUPT_ERROR;
793 fs->generic.out.fs_id = st.st_ino;
794 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
795 fs->generic.out.serial_number = st.st_ino;
796 fs->generic.out.fs_attr = 0;
797 fs->generic.out.max_file_component_length = 255;
798 fs->generic.out.device_type = 0;
799 fs->generic.out.device_characteristics = 0;
800 fs->generic.out.quota_soft = 0;
801 fs->generic.out.quota_hard = 0;
802 fs->generic.out.quota_flags = 0;
803 fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
804 fs->generic.out.fs_type = ntvfs->ctx->fs_type;
806 return NT_STATUS_OK;
809 #if 0
811 return filesystem attribute info
813 static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
814 struct ntvfs_request *req, union smb_fsattr *fs)
816 struct stat st;
817 struct svfs_private *p = ntvfs->private_data;
819 if (fs->generic.level != RAW_FSATTR_GENERIC) {
820 return ntvfs_map_fsattr(ntvfs, req, fs);
823 if (stat(p->connectpath, &st) == -1) {
824 return map_nt_error_from_unix(errno);
827 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
828 fs->generic.out.fs_attr =
829 FILE_CASE_PRESERVED_NAMES |
830 FILE_CASE_SENSITIVE_SEARCH |
831 FILE_PERSISTENT_ACLS;
832 fs->generic.out.max_file_component_length = 255;
833 fs->generic.out.serial_number = 1;
834 fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
835 fs->generic.out.volume_name = talloc_strdup(req,
836 lpcfg_servicename(req->tcon->service));
838 return NT_STATUS_OK;
840 #endif
843 return print queue info
845 static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
846 struct ntvfs_request *req, union smb_lpq *lpq)
848 return NT_STATUS_NOT_SUPPORTED;
852 list files in a directory matching a wildcard pattern
854 static NTSTATUS svfs_search_first(struct ntvfs_module_context *ntvfs,
855 struct ntvfs_request *req, union smb_search_first *io,
856 void *search_private,
857 bool (*callback)(void *, const union smb_search_data *))
859 struct svfs_dir *dir;
860 int i;
861 struct svfs_private *p = ntvfs->private_data;
862 struct search_state *search;
863 union smb_search_data file;
864 unsigned int max_count;
866 if (io->generic.level != RAW_SEARCH_TRANS2) {
867 return NT_STATUS_NOT_SUPPORTED;
870 if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
871 return NT_STATUS_NOT_SUPPORTED;
874 search = talloc_zero(p, struct search_state);
875 if (!search) {
876 return NT_STATUS_NO_MEMORY;
879 max_count = io->t2ffirst.in.max_count;
881 dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
882 if (!dir) {
883 return NT_STATUS_FOOBAR;
886 search->handle = p->next_search_handle;
887 search->dir = dir;
889 if (dir->count < max_count) {
890 max_count = dir->count;
893 for (i=0; i < max_count;i++) {
894 ZERO_STRUCT(file);
895 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
896 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
897 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
898 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
899 file.both_directory_info.name.s = dir->files[i].name;
900 file.both_directory_info.short_name.s = dir->files[i].name;
901 file.both_directory_info.size = dir->files[i].st.st_size;
902 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
904 if (!callback(search_private, &file)) {
905 break;
909 search->current_index = i;
911 io->t2ffirst.out.count = i;
912 io->t2ffirst.out.handle = search->handle;
913 io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
915 /* work out if we are going to keep the search state */
916 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
917 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
918 talloc_free(search);
919 } else {
920 p->next_search_handle++;
921 DLIST_ADD(p->search, search);
924 return NT_STATUS_OK;
927 /* continue a search */
928 static NTSTATUS svfs_search_next(struct ntvfs_module_context *ntvfs,
929 struct ntvfs_request *req, union smb_search_next *io,
930 void *search_private,
931 bool (*callback)(void *, const union smb_search_data *))
933 struct svfs_dir *dir;
934 int i;
935 struct svfs_private *p = ntvfs->private_data;
936 struct search_state *search;
937 union smb_search_data file;
938 unsigned int max_count;
940 if (io->generic.level != RAW_SEARCH_TRANS2) {
941 return NT_STATUS_NOT_SUPPORTED;
944 if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
945 return NT_STATUS_NOT_SUPPORTED;
948 for (search=p->search; search; search = search->next) {
949 if (search->handle == io->t2fnext.in.handle) break;
952 if (!search) {
953 /* we didn't find the search handle */
954 return NT_STATUS_FOOBAR;
957 dir = search->dir;
959 /* the client might be asking for something other than just continuing
960 with the search */
961 if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
962 (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
963 io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
964 /* look backwards first */
965 for (i=search->current_index; i > 0; i--) {
966 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
967 search->current_index = i;
968 goto found;
972 /* then look forwards */
973 for (i=search->current_index+1; i <= dir->count; i++) {
974 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
975 search->current_index = i;
976 goto found;
981 found:
982 max_count = search->current_index + io->t2fnext.in.max_count;
984 if (max_count > dir->count) {
985 max_count = dir->count;
988 for (i = search->current_index; i < max_count;i++) {
989 ZERO_STRUCT(file);
990 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
991 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
992 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
993 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
994 file.both_directory_info.name.s = dir->files[i].name;
995 file.both_directory_info.short_name.s = dir->files[i].name;
996 file.both_directory_info.size = dir->files[i].st.st_size;
997 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
999 if (!callback(search_private, &file)) {
1000 break;
1004 io->t2fnext.out.count = i - search->current_index;
1005 io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
1007 search->current_index = i;
1009 /* work out if we are going to keep the search state */
1010 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
1011 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
1012 DLIST_REMOVE(p->search, search);
1013 talloc_free(search);
1016 return NT_STATUS_OK;
1019 /* close a search */
1020 static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
1021 struct ntvfs_request *req, union smb_search_close *io)
1023 struct svfs_private *p = ntvfs->private_data;
1024 struct search_state *search;
1026 for (search=p->search; search; search = search->next) {
1027 if (search->handle == io->findclose.in.handle) break;
1030 if (!search) {
1031 /* we didn't find the search handle */
1032 return NT_STATUS_FOOBAR;
1035 DLIST_REMOVE(p->search, search);
1036 talloc_free(search);
1038 return NT_STATUS_OK;
1041 /* SMBtrans - not used on file shares */
1042 static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
1043 struct ntvfs_request *req, struct smb_trans2 *trans2)
1045 return NT_STATUS_ACCESS_DENIED;
1050 initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
1052 NTSTATUS ntvfs_simple_init(void)
1054 NTSTATUS ret;
1055 struct ntvfs_ops ops;
1056 NTVFS_CURRENT_CRITICAL_SIZES(vers);
1058 ZERO_STRUCT(ops);
1060 /* fill in all the operations */
1061 ops.connect = svfs_connect;
1062 ops.disconnect = svfs_disconnect;
1063 ops.unlink = svfs_unlink;
1064 ops.chkpath = svfs_chkpath;
1065 ops.qpathinfo = svfs_qpathinfo;
1066 ops.setpathinfo = svfs_setpathinfo;
1067 ops.open = svfs_open;
1068 ops.mkdir = svfs_mkdir;
1069 ops.rmdir = svfs_rmdir;
1070 ops.rename = svfs_rename;
1071 ops.copy = svfs_copy;
1072 ops.ioctl = svfs_ioctl;
1073 ops.read = svfs_read;
1074 ops.write = svfs_write;
1075 ops.seek = svfs_seek;
1076 ops.flush = svfs_flush;
1077 ops.close = svfs_close;
1078 ops.exit = svfs_exit;
1079 ops.lock = svfs_lock;
1080 ops.setfileinfo = svfs_setfileinfo;
1081 ops.qfileinfo = svfs_qfileinfo;
1082 ops.fsinfo = svfs_fsinfo;
1083 ops.lpq = svfs_lpq;
1084 ops.search_first = svfs_search_first;
1085 ops.search_next = svfs_search_next;
1086 ops.search_close = svfs_search_close;
1087 ops.trans = svfs_trans;
1088 ops.logoff = svfs_logoff;
1089 ops.async_setup = svfs_async_setup;
1090 ops.cancel = svfs_cancel;
1092 /* register ourselves with the NTVFS subsystem. We register
1093 under names 'simple'
1096 ops.type = NTVFS_DISK;
1097 ops.name = "simple";
1098 ret = ntvfs_register(&ops, &vers);
1100 if (!NT_STATUS_IS_OK(ret)) {
1101 DEBUG(0,("Failed to register simple backend with name: %s!\n",
1102 ops.name));
1105 return ret;