ctdb-server: Remove duplicate logic
[samba4-gss.git] / source4 / ntvfs / simple / vfs_simple.c
blob794f06d3e06e612d8cdbc39a31dbe2e7a18f431b
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 = share_string_option(p, 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_common(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_common(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, ret;
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 ret = asprintf(&pattern, "%s:*", unix_path);
220 if (ret == -1) {
221 return NT_STATUS_NO_MEMORY;
224 if (pattern) {
225 dir = svfs_list_unix(req, req, pattern);
228 unix_to_nt_time(&info->generic.out.create_time, st->st_ctime);
229 unix_to_nt_time(&info->generic.out.access_time, st->st_atime);
230 unix_to_nt_time(&info->generic.out.write_time, st->st_mtime);
231 unix_to_nt_time(&info->generic.out.change_time, st->st_mtime);
232 info->generic.out.alloc_size = st->st_size;
233 info->generic.out.size = st->st_size;
234 info->generic.out.attrib = svfs_unix_to_dos_attrib(st->st_mode);
235 info->generic.out.alloc_size = st->st_blksize * st->st_blocks;
236 info->generic.out.nlink = st->st_nlink;
237 info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
238 info->generic.out.file_id = svfs_file_id(st);
239 /* REWRITE: TODO stuff in here */
240 info->generic.out.delete_pending = 0;
241 info->generic.out.ea_size = 0;
242 info->generic.out.num_eas = 0;
243 info->generic.out.fname.s = talloc_strdup(req, short_name);
244 info->generic.out.alt_fname.s = talloc_strdup(req, short_name);
245 info->generic.out.compressed_size = 0;
246 info->generic.out.format = 0;
247 info->generic.out.unit_shift = 0;
248 info->generic.out.chunk_shift = 0;
249 info->generic.out.cluster_shift = 0;
251 info->generic.out.access_flags = 0;
252 info->generic.out.position = 0;
253 info->generic.out.mode = 0;
254 info->generic.out.alignment_requirement = 0;
255 info->generic.out.reparse_tag = 0;
256 info->generic.out.num_streams = 0;
257 /* setup a single data stream */
258 info->generic.out.num_streams = 1 + (dir?dir->count:0);
259 info->generic.out.streams = talloc_array(req,
260 struct stream_struct,
261 info->generic.out.num_streams);
262 if (!info->generic.out.streams) {
263 return NT_STATUS_NO_MEMORY;
265 info->generic.out.streams[0].size = st->st_size;
266 info->generic.out.streams[0].alloc_size = st->st_size;
267 info->generic.out.streams[0].stream_name.s = talloc_strdup(req,"::$DATA");
269 for (i=0;dir && i<dir->count;i++) {
270 s = strchr(dir->files[i].name, ':');
271 info->generic.out.streams[1+i].size = dir->files[i].st.st_size;
272 info->generic.out.streams[1+i].alloc_size = dir->files[i].st.st_size;
273 info->generic.out.streams[1+i].stream_name.s = s?s:dir->files[i].name;
276 return NT_STATUS_OK;
280 return info on a pathname
282 static NTSTATUS svfs_qpathinfo(struct ntvfs_module_context *ntvfs,
283 struct ntvfs_request *req, union smb_fileinfo *info)
285 char *unix_path;
286 struct stat st;
288 DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.file.path, info->generic.level));
289 if (info->generic.level != RAW_FILEINFO_GENERIC) {
290 return ntvfs_map_qpathinfo(ntvfs, req, info);
293 unix_path = svfs_unix_path(ntvfs, req, info->generic.in.file.path);
294 DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path));
295 if (stat(unix_path, &st) == -1) {
296 DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno));
297 return map_nt_error_from_unix_common(errno);
299 DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path));
300 return svfs_map_fileinfo(ntvfs, req, info, &st, unix_path);
304 query info on a open file
306 static NTSTATUS svfs_qfileinfo(struct ntvfs_module_context *ntvfs,
307 struct ntvfs_request *req, union smb_fileinfo *info)
309 struct svfs_private *p = ntvfs->private_data;
310 struct svfs_file *f;
311 struct stat st;
313 if (info->generic.level != RAW_FILEINFO_GENERIC) {
314 return ntvfs_map_qfileinfo(ntvfs, req, info);
317 f = find_fd(p, info->generic.in.file.ntvfs);
318 if (!f) {
319 return NT_STATUS_INVALID_HANDLE;
322 if (fstat(f->fd, &st) == -1) {
323 return map_nt_error_from_unix_common(errno);
326 return svfs_map_fileinfo(ntvfs, req,info, &st, f->name);
331 open a file
333 static NTSTATUS svfs_open(struct ntvfs_module_context *ntvfs,
334 struct ntvfs_request *req, union smb_open *io)
336 struct svfs_private *p = ntvfs->private_data;
337 char *unix_path;
338 struct stat st;
339 int fd, flags;
340 struct svfs_file *f;
341 int create_flags, rdwr_flags;
342 bool readonly;
343 NTSTATUS status;
344 struct ntvfs_handle *handle;
346 if (io->generic.level != RAW_OPEN_GENERIC) {
347 return ntvfs_map_open(ntvfs, req, io);
350 readonly = share_bool_option(ntvfs->ctx->config, SHARE_READONLY, SHARE_READONLY_DEFAULT);
351 if (readonly) {
352 create_flags = 0;
353 rdwr_flags = O_RDONLY;
354 } else {
355 create_flags = O_CREAT;
356 rdwr_flags = O_RDWR;
359 unix_path = svfs_unix_path(ntvfs, req, io->ntcreatex.in.fname);
361 switch (io->generic.in.open_disposition) {
362 case NTCREATEX_DISP_SUPERSEDE:
363 case NTCREATEX_DISP_OVERWRITE_IF:
364 flags = create_flags | O_TRUNC;
365 break;
366 case NTCREATEX_DISP_OPEN:
367 case NTCREATEX_DISP_OVERWRITE:
368 flags = 0;
369 break;
370 case NTCREATEX_DISP_CREATE:
371 flags = create_flags | O_EXCL;
372 break;
373 case NTCREATEX_DISP_OPEN_IF:
374 flags = create_flags;
375 break;
376 default:
377 flags = 0;
378 break;
381 flags |= rdwr_flags;
383 if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
384 flags = O_RDONLY | O_DIRECTORY;
385 if (readonly) {
386 goto do_open;
388 switch (io->generic.in.open_disposition) {
389 case NTCREATEX_DISP_CREATE:
390 if (mkdir(unix_path, 0755) == -1) {
391 DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
392 return map_nt_error_from_unix_common(errno);
394 break;
395 case NTCREATEX_DISP_OPEN_IF:
396 if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
397 DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
398 return map_nt_error_from_unix_common(errno);
400 break;
404 do_open:
405 fd = open(unix_path, flags, 0644);
406 if (fd == -1) {
407 return map_nt_error_from_unix_common(errno);
410 if (fstat(fd, &st) == -1) {
411 DEBUG(9,("svfs_open: fstat errno=%d\n", errno));
412 close(fd);
413 return map_nt_error_from_unix_common(errno);
416 status = ntvfs_handle_new(ntvfs, req, &handle);
417 if (!NT_STATUS_IS_OK(status)) {
418 close(fd);
419 return status;
422 f = talloc(handle, struct svfs_file);
423 if (f == NULL) {
424 close(fd);
425 return NT_STATUS_NO_MEMORY;
427 f->fd = fd;
428 f->name = talloc_strdup(f, unix_path);
429 NT_STATUS_HAVE_NO_MEMORY(f->name);
431 DLIST_ADD(p->open_files, f);
433 status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
434 NT_STATUS_NOT_OK_RETURN(status);
436 ZERO_STRUCT(io->generic.out);
438 unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
439 unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
440 unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
441 unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
442 io->generic.out.file.ntvfs = handle;
443 io->generic.out.alloc_size = st.st_size;
444 io->generic.out.size = st.st_size;
445 io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
446 io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
448 return NT_STATUS_OK;
452 create a directory
454 static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
455 struct ntvfs_request *req, union smb_mkdir *md)
457 char *unix_path;
459 CHECK_READ_ONLY(req);
461 if (md->generic.level != RAW_MKDIR_MKDIR) {
462 return NT_STATUS_INVALID_LEVEL;
465 unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
467 if (mkdir(unix_path, 0777) == -1) {
468 return map_nt_error_from_unix_common(errno);
471 return NT_STATUS_OK;
475 remove a directory
477 static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
478 struct ntvfs_request *req, struct smb_rmdir *rd)
480 char *unix_path;
482 CHECK_READ_ONLY(req);
484 unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
486 if (rmdir(unix_path) == -1) {
487 return map_nt_error_from_unix_common(errno);
490 return NT_STATUS_OK;
494 rename a set of files
496 static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
497 struct ntvfs_request *req, union smb_rename *ren)
499 char *unix_path1, *unix_path2;
501 CHECK_READ_ONLY(req);
503 if (ren->generic.level != RAW_RENAME_RENAME) {
504 return NT_STATUS_INVALID_LEVEL;
507 unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
508 unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
510 if (rename(unix_path1, unix_path2) == -1) {
511 return map_nt_error_from_unix_common(errno);
514 return NT_STATUS_OK;
518 copy a set of files
520 static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
521 struct ntvfs_request *req, struct smb_copy *cp)
523 return NT_STATUS_NOT_SUPPORTED;
527 read from a file
529 static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
530 struct ntvfs_request *req, union smb_read *rd)
532 struct svfs_private *p = ntvfs->private_data;
533 struct svfs_file *f;
534 ssize_t ret;
536 if (rd->generic.level != RAW_READ_READX) {
537 return NT_STATUS_NOT_SUPPORTED;
540 f = find_fd(p, rd->readx.in.file.ntvfs);
541 if (!f) {
542 return NT_STATUS_INVALID_HANDLE;
545 ret = pread(f->fd,
546 rd->readx.out.data,
547 rd->readx.in.maxcnt,
548 rd->readx.in.offset);
549 if (ret == -1) {
550 return map_nt_error_from_unix_common(errno);
553 rd->readx.out.nread = ret;
554 rd->readx.out.remaining = 0; /* should fill this in? */
555 rd->readx.out.compaction_mode = 0;
557 return NT_STATUS_OK;
561 write to a file
563 static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
564 struct ntvfs_request *req, union smb_write *wr)
566 struct svfs_private *p = ntvfs->private_data;
567 struct svfs_file *f;
568 ssize_t ret;
570 if (wr->generic.level != RAW_WRITE_WRITEX) {
571 return ntvfs_map_write(ntvfs, req, wr);
574 CHECK_READ_ONLY(req);
576 f = find_fd(p, wr->writex.in.file.ntvfs);
577 if (!f) {
578 return NT_STATUS_INVALID_HANDLE;
581 ret = pwrite(f->fd,
582 wr->writex.in.data,
583 wr->writex.in.count,
584 wr->writex.in.offset);
585 if (ret == -1) {
586 return map_nt_error_from_unix_common(errno);
589 wr->writex.out.nwritten = ret;
590 wr->writex.out.remaining = 0; /* should fill this in? */
592 return NT_STATUS_OK;
596 seek in a file
598 static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
599 struct ntvfs_request *req,
600 union smb_seek *io)
602 return NT_STATUS_NOT_SUPPORTED;
606 flush a file
608 static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
609 struct ntvfs_request *req,
610 union smb_flush *io)
612 struct svfs_private *p = ntvfs->private_data;
613 struct svfs_file *f;
615 switch (io->generic.level) {
616 case RAW_FLUSH_FLUSH:
617 case RAW_FLUSH_SMB2:
618 /* ignore the additional unknown option in SMB2 */
619 f = find_fd(p, io->generic.in.file.ntvfs);
620 if (!f) {
621 return NT_STATUS_INVALID_HANDLE;
623 fsync(f->fd);
624 return NT_STATUS_OK;
626 case RAW_FLUSH_ALL:
627 for (f=p->open_files;f;f=f->next) {
628 fsync(f->fd);
630 return NT_STATUS_OK;
633 return NT_STATUS_INVALID_LEVEL;
637 close a file
639 static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
640 struct ntvfs_request *req,
641 union smb_close *io)
643 struct svfs_private *p = ntvfs->private_data;
644 struct svfs_file *f;
646 if (io->generic.level != RAW_CLOSE_CLOSE) {
647 /* we need a mapping function */
648 return NT_STATUS_INVALID_LEVEL;
651 f = find_fd(p, io->close.in.file.ntvfs);
652 if (!f) {
653 return NT_STATUS_INVALID_HANDLE;
656 if (close(f->fd) == -1) {
657 return map_nt_error_from_unix_common(errno);
660 DLIST_REMOVE(p->open_files, f);
661 talloc_free(f->name);
662 talloc_free(f);
664 return NT_STATUS_OK;
668 exit - closing files
670 static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
671 struct ntvfs_request *req)
673 return NT_STATUS_NOT_SUPPORTED;
677 logoff - closing files
679 static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
680 struct ntvfs_request *req)
682 return NT_STATUS_NOT_SUPPORTED;
686 setup for an async call
688 static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
689 struct ntvfs_request *req,
690 void *private_data)
692 return NT_STATUS_OK;
696 cancel an async call
698 static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
700 return NT_STATUS_UNSUCCESSFUL;
704 lock a byte range
706 static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
707 struct ntvfs_request *req, union smb_lock *lck)
709 DEBUG(0,("REWRITE: not doing byte range locking!\n"));
710 return NT_STATUS_OK;
714 set info on a pathname
716 static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
717 struct ntvfs_request *req, union smb_setfileinfo *st)
719 CHECK_READ_ONLY(req);
721 return NT_STATUS_NOT_SUPPORTED;
725 set info on a open file
727 static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
728 struct ntvfs_request *req,
729 union smb_setfileinfo *info)
731 struct svfs_private *p = ntvfs->private_data;
732 struct svfs_file *f;
733 struct utimbuf unix_times;
735 CHECK_READ_ONLY(req);
737 f = find_fd(p, info->generic.in.file.ntvfs);
738 if (!f) {
739 return NT_STATUS_INVALID_HANDLE;
742 switch (info->generic.level) {
743 case RAW_SFILEINFO_END_OF_FILE_INFO:
744 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
745 if (ftruncate(f->fd,
746 info->end_of_file_info.in.size) == -1) {
747 return map_nt_error_from_unix_common(errno);
749 break;
750 case RAW_SFILEINFO_SETATTRE:
751 unix_times.actime = info->setattre.in.access_time;
752 unix_times.modtime = info->setattre.in.write_time;
754 if (unix_times.actime == 0 && unix_times.modtime == 0) {
755 break;
758 /* set modify time = to access time if modify time was 0 */
759 if (unix_times.actime != 0 && unix_times.modtime == 0) {
760 unix_times.modtime = unix_times.actime;
763 /* Set the date on this file */
764 if (svfs_file_utime(f->fd, &unix_times) != 0) {
765 return NT_STATUS_ACCESS_DENIED;
767 break;
768 default:
769 DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
770 info->generic.level));
771 return NT_STATUS_NOT_IMPLEMENTED;
773 return NT_STATUS_OK;
778 return filesystem space info
780 static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
781 struct ntvfs_request *req, union smb_fsinfo *fs)
783 struct svfs_private *p = ntvfs->private_data;
784 struct stat st;
786 if (fs->generic.level != RAW_QFS_GENERIC) {
787 return ntvfs_map_fsinfo(ntvfs, req, fs);
790 if (sys_fsusage(p->connectpath,
791 &fs->generic.out.blocks_free,
792 &fs->generic.out.blocks_total) == -1) {
793 return map_nt_error_from_unix_common(errno);
796 fs->generic.out.block_size = 512;
798 if (stat(p->connectpath, &st) != 0) {
799 return NT_STATUS_DISK_CORRUPT_ERROR;
802 fs->generic.out.fs_id = st.st_ino;
803 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
804 fs->generic.out.serial_number = st.st_ino;
805 fs->generic.out.fs_attr = 0;
806 fs->generic.out.max_file_component_length = 255;
807 fs->generic.out.device_type = 0;
808 fs->generic.out.device_characteristics = 0;
809 fs->generic.out.quota_soft = 0;
810 fs->generic.out.quota_hard = 0;
811 fs->generic.out.quota_flags = 0;
812 fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
813 fs->generic.out.fs_type = ntvfs->ctx->fs_type;
815 return NT_STATUS_OK;
818 #if 0
820 return filesystem attribute info
822 static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
823 struct ntvfs_request *req, union smb_fsattr *fs)
825 struct stat st;
826 struct svfs_private *p = ntvfs->private_data;
828 if (fs->generic.level != RAW_FSATTR_GENERIC) {
829 return ntvfs_map_fsattr(ntvfs, req, fs);
832 if (stat(p->connectpath, &st) == -1) {
833 return map_nt_error_from_unix_common(errno);
836 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
837 fs->generic.out.fs_attr =
838 FILE_CASE_PRESERVED_NAMES |
839 FILE_CASE_SENSITIVE_SEARCH |
840 FILE_PERSISTENT_ACLS;
841 fs->generic.out.max_file_component_length = 255;
842 fs->generic.out.serial_number = 1;
843 fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
844 fs->generic.out.volume_name = talloc_strdup(req,
845 lpcfg_servicename(req->tcon->service));
847 return NT_STATUS_OK;
849 #endif
852 return print queue info
854 static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
855 struct ntvfs_request *req, union smb_lpq *lpq)
857 return NT_STATUS_NOT_SUPPORTED;
861 list files in a directory matching a wildcard pattern
863 static NTSTATUS svfs_search_first(struct ntvfs_module_context *ntvfs,
864 struct ntvfs_request *req, union smb_search_first *io,
865 void *search_private,
866 bool (*callback)(void *, const union smb_search_data *))
868 struct svfs_dir *dir;
869 int i;
870 struct svfs_private *p = ntvfs->private_data;
871 struct search_state *search;
872 union smb_search_data file;
873 unsigned int max_count;
875 if (io->generic.level != RAW_SEARCH_TRANS2) {
876 return NT_STATUS_NOT_SUPPORTED;
879 if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
880 return NT_STATUS_NOT_SUPPORTED;
883 search = talloc_zero(p, struct search_state);
884 if (!search) {
885 return NT_STATUS_NO_MEMORY;
888 max_count = io->t2ffirst.in.max_count;
890 dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
891 if (!dir) {
892 return NT_STATUS_FOOBAR;
895 search->handle = p->next_search_handle;
896 search->dir = dir;
898 if (dir->count < max_count) {
899 max_count = dir->count;
902 for (i=0; i < max_count;i++) {
903 ZERO_STRUCT(file);
904 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
905 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
906 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
907 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
908 file.both_directory_info.name.s = dir->files[i].name;
909 file.both_directory_info.short_name.s = dir->files[i].name;
910 file.both_directory_info.size = dir->files[i].st.st_size;
911 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
913 if (!callback(search_private, &file)) {
914 break;
918 search->current_index = i;
920 io->t2ffirst.out.count = i;
921 io->t2ffirst.out.handle = search->handle;
922 io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
924 /* work out if we are going to keep the search state */
925 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
926 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
927 talloc_free(search);
928 } else {
929 p->next_search_handle++;
930 DLIST_ADD(p->search, search);
933 return NT_STATUS_OK;
936 /* continue a search */
937 static NTSTATUS svfs_search_next(struct ntvfs_module_context *ntvfs,
938 struct ntvfs_request *req, union smb_search_next *io,
939 void *search_private,
940 bool (*callback)(void *, const union smb_search_data *))
942 struct svfs_dir *dir;
943 int i;
944 struct svfs_private *p = ntvfs->private_data;
945 struct search_state *search;
946 union smb_search_data file;
947 unsigned int max_count;
949 if (io->generic.level != RAW_SEARCH_TRANS2) {
950 return NT_STATUS_NOT_SUPPORTED;
953 if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
954 return NT_STATUS_NOT_SUPPORTED;
957 for (search=p->search; search; search = search->next) {
958 if (search->handle == io->t2fnext.in.handle) break;
961 if (!search) {
962 /* we didn't find the search handle */
963 return NT_STATUS_FOOBAR;
966 dir = search->dir;
968 /* the client might be asking for something other than just continuing
969 with the search */
970 if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
971 (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
972 io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
973 /* look backwards first */
974 for (i=search->current_index; i > 0; i--) {
975 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
976 search->current_index = i;
977 goto found;
981 /* then look forwards */
982 for (i=search->current_index+1; i <= dir->count; i++) {
983 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
984 search->current_index = i;
985 goto found;
990 found:
991 max_count = search->current_index + io->t2fnext.in.max_count;
993 if (max_count > dir->count) {
994 max_count = dir->count;
997 for (i = search->current_index; i < max_count;i++) {
998 ZERO_STRUCT(file);
999 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
1000 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
1001 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
1002 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
1003 file.both_directory_info.name.s = dir->files[i].name;
1004 file.both_directory_info.short_name.s = dir->files[i].name;
1005 file.both_directory_info.size = dir->files[i].st.st_size;
1006 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
1008 if (!callback(search_private, &file)) {
1009 break;
1013 io->t2fnext.out.count = i - search->current_index;
1014 io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
1016 search->current_index = i;
1018 /* work out if we are going to keep the search state */
1019 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
1020 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
1021 DLIST_REMOVE(p->search, search);
1022 talloc_free(search);
1025 return NT_STATUS_OK;
1028 /* close a search */
1029 static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
1030 struct ntvfs_request *req, union smb_search_close *io)
1032 struct svfs_private *p = ntvfs->private_data;
1033 struct search_state *search;
1035 for (search=p->search; search; search = search->next) {
1036 if (search->handle == io->findclose.in.handle) break;
1039 if (!search) {
1040 /* we didn't find the search handle */
1041 return NT_STATUS_FOOBAR;
1044 DLIST_REMOVE(p->search, search);
1045 talloc_free(search);
1047 return NT_STATUS_OK;
1050 /* SMBtrans - not used on file shares */
1051 static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
1052 struct ntvfs_request *req, struct smb_trans2 *trans2)
1054 return NT_STATUS_ACCESS_DENIED;
1059 initialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
1061 NTSTATUS ntvfs_simple_init(TALLOC_CTX *ctx)
1063 NTSTATUS ret;
1064 struct ntvfs_ops ops;
1065 NTVFS_CURRENT_CRITICAL_SIZES(vers);
1067 ZERO_STRUCT(ops);
1069 /* fill in all the operations */
1070 ops.connect_fn = svfs_connect;
1071 ops.disconnect_fn = svfs_disconnect;
1072 ops.unlink_fn = svfs_unlink;
1073 ops.chkpath_fn = svfs_chkpath;
1074 ops.qpathinfo_fn = svfs_qpathinfo;
1075 ops.setpathinfo_fn = svfs_setpathinfo;
1076 ops.open_fn = svfs_open;
1077 ops.mkdir_fn = svfs_mkdir;
1078 ops.rmdir_fn = svfs_rmdir;
1079 ops.rename_fn = svfs_rename;
1080 ops.copy_fn = svfs_copy;
1081 ops.ioctl_fn = svfs_ioctl;
1082 ops.read_fn = svfs_read;
1083 ops.write_fn = svfs_write;
1084 ops.seek_fn = svfs_seek;
1085 ops.flush_fn = svfs_flush;
1086 ops.close_fn = svfs_close;
1087 ops.exit_fn = svfs_exit;
1088 ops.lock_fn = svfs_lock;
1089 ops.setfileinfo_fn = svfs_setfileinfo;
1090 ops.qfileinfo_fn = svfs_qfileinfo;
1091 ops.fsinfo_fn = svfs_fsinfo;
1092 ops.lpq_fn = svfs_lpq;
1093 ops.search_first_fn = svfs_search_first;
1094 ops.search_next_fn = svfs_search_next;
1095 ops.search_close_fn = svfs_search_close;
1096 ops.trans_fn = svfs_trans;
1097 ops.logoff_fn = svfs_logoff;
1098 ops.async_setup_fn = svfs_async_setup;
1099 ops.cancel_fn = svfs_cancel;
1101 /* register ourselves with the NTVFS subsystem. We register
1102 under names 'simple'
1105 ops.type = NTVFS_DISK;
1106 ops.name = "simple";
1107 ret = ntvfs_register(&ops, &vers);
1109 if (!NT_STATUS_IS_OK(ret)) {
1110 DEBUG(0,("Failed to register simple backend with name: %s!\n",
1111 ops.name));
1114 return ret;