scripting: Add script (backportable) to undo a GUID index
[Samba.git] / source4 / ntvfs / simple / vfs_simple.c
blob2fe208448212402cc0a34207849575c957014595
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 NT_STATUS_NOT_OK_RETURN(status);
419 f = talloc(handle, struct svfs_file);
420 if (f == NULL) {
421 close(fd);
422 return NT_STATUS_NO_MEMORY;
424 f->fd = fd;
425 f->name = talloc_strdup(f, unix_path);
426 NT_STATUS_HAVE_NO_MEMORY(f->name);
428 DLIST_ADD(p->open_files, f);
430 status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
431 NT_STATUS_NOT_OK_RETURN(status);
433 ZERO_STRUCT(io->generic.out);
435 unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
436 unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
437 unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
438 unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
439 io->generic.out.file.ntvfs = handle;
440 io->generic.out.alloc_size = st.st_size;
441 io->generic.out.size = st.st_size;
442 io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
443 io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
445 return NT_STATUS_OK;
449 create a directory
451 static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
452 struct ntvfs_request *req, union smb_mkdir *md)
454 char *unix_path;
456 CHECK_READ_ONLY(req);
458 if (md->generic.level != RAW_MKDIR_MKDIR) {
459 return NT_STATUS_INVALID_LEVEL;
462 unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
464 if (mkdir(unix_path, 0777) == -1) {
465 return map_nt_error_from_unix_common(errno);
468 return NT_STATUS_OK;
472 remove a directory
474 static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
475 struct ntvfs_request *req, struct smb_rmdir *rd)
477 char *unix_path;
479 CHECK_READ_ONLY(req);
481 unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
483 if (rmdir(unix_path) == -1) {
484 return map_nt_error_from_unix_common(errno);
487 return NT_STATUS_OK;
491 rename a set of files
493 static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
494 struct ntvfs_request *req, union smb_rename *ren)
496 char *unix_path1, *unix_path2;
498 CHECK_READ_ONLY(req);
500 if (ren->generic.level != RAW_RENAME_RENAME) {
501 return NT_STATUS_INVALID_LEVEL;
504 unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
505 unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
507 if (rename(unix_path1, unix_path2) == -1) {
508 return map_nt_error_from_unix_common(errno);
511 return NT_STATUS_OK;
515 copy a set of files
517 static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
518 struct ntvfs_request *req, struct smb_copy *cp)
520 return NT_STATUS_NOT_SUPPORTED;
524 read from a file
526 static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
527 struct ntvfs_request *req, union smb_read *rd)
529 struct svfs_private *p = ntvfs->private_data;
530 struct svfs_file *f;
531 ssize_t ret;
533 if (rd->generic.level != RAW_READ_READX) {
534 return NT_STATUS_NOT_SUPPORTED;
537 f = find_fd(p, rd->readx.in.file.ntvfs);
538 if (!f) {
539 return NT_STATUS_INVALID_HANDLE;
542 ret = pread(f->fd,
543 rd->readx.out.data,
544 rd->readx.in.maxcnt,
545 rd->readx.in.offset);
546 if (ret == -1) {
547 return map_nt_error_from_unix_common(errno);
550 rd->readx.out.nread = ret;
551 rd->readx.out.remaining = 0; /* should fill this in? */
552 rd->readx.out.compaction_mode = 0;
554 return NT_STATUS_OK;
558 write to a file
560 static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
561 struct ntvfs_request *req, union smb_write *wr)
563 struct svfs_private *p = ntvfs->private_data;
564 struct svfs_file *f;
565 ssize_t ret;
567 if (wr->generic.level != RAW_WRITE_WRITEX) {
568 return ntvfs_map_write(ntvfs, req, wr);
571 CHECK_READ_ONLY(req);
573 f = find_fd(p, wr->writex.in.file.ntvfs);
574 if (!f) {
575 return NT_STATUS_INVALID_HANDLE;
578 ret = pwrite(f->fd,
579 wr->writex.in.data,
580 wr->writex.in.count,
581 wr->writex.in.offset);
582 if (ret == -1) {
583 return map_nt_error_from_unix_common(errno);
586 wr->writex.out.nwritten = ret;
587 wr->writex.out.remaining = 0; /* should fill this in? */
589 return NT_STATUS_OK;
593 seek in a file
595 static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
596 struct ntvfs_request *req,
597 union smb_seek *io)
599 return NT_STATUS_NOT_SUPPORTED;
603 flush a file
605 static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
606 struct ntvfs_request *req,
607 union smb_flush *io)
609 struct svfs_private *p = ntvfs->private_data;
610 struct svfs_file *f;
612 switch (io->generic.level) {
613 case RAW_FLUSH_FLUSH:
614 case RAW_FLUSH_SMB2:
615 /* ignore the additional unknown option in SMB2 */
616 f = find_fd(p, io->generic.in.file.ntvfs);
617 if (!f) {
618 return NT_STATUS_INVALID_HANDLE;
620 fsync(f->fd);
621 return NT_STATUS_OK;
623 case RAW_FLUSH_ALL:
624 for (f=p->open_files;f;f=f->next) {
625 fsync(f->fd);
627 return NT_STATUS_OK;
630 return NT_STATUS_INVALID_LEVEL;
634 close a file
636 static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
637 struct ntvfs_request *req,
638 union smb_close *io)
640 struct svfs_private *p = ntvfs->private_data;
641 struct svfs_file *f;
643 if (io->generic.level != RAW_CLOSE_CLOSE) {
644 /* we need a mapping function */
645 return NT_STATUS_INVALID_LEVEL;
648 f = find_fd(p, io->close.in.file.ntvfs);
649 if (!f) {
650 return NT_STATUS_INVALID_HANDLE;
653 if (close(f->fd) == -1) {
654 return map_nt_error_from_unix_common(errno);
657 DLIST_REMOVE(p->open_files, f);
658 talloc_free(f->name);
659 talloc_free(f);
661 return NT_STATUS_OK;
665 exit - closing files
667 static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
668 struct ntvfs_request *req)
670 return NT_STATUS_NOT_SUPPORTED;
674 logoff - closing files
676 static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
677 struct ntvfs_request *req)
679 return NT_STATUS_NOT_SUPPORTED;
683 setup for an async call
685 static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
686 struct ntvfs_request *req,
687 void *private_data)
689 return NT_STATUS_OK;
693 cancel an async call
695 static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
697 return NT_STATUS_UNSUCCESSFUL;
701 lock a byte range
703 static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
704 struct ntvfs_request *req, union smb_lock *lck)
706 DEBUG(0,("REWRITE: not doing byte range locking!\n"));
707 return NT_STATUS_OK;
711 set info on a pathname
713 static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
714 struct ntvfs_request *req, union smb_setfileinfo *st)
716 CHECK_READ_ONLY(req);
718 return NT_STATUS_NOT_SUPPORTED;
722 set info on a open file
724 static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
725 struct ntvfs_request *req,
726 union smb_setfileinfo *info)
728 struct svfs_private *p = ntvfs->private_data;
729 struct svfs_file *f;
730 struct utimbuf unix_times;
732 CHECK_READ_ONLY(req);
734 f = find_fd(p, info->generic.in.file.ntvfs);
735 if (!f) {
736 return NT_STATUS_INVALID_HANDLE;
739 switch (info->generic.level) {
740 case RAW_SFILEINFO_END_OF_FILE_INFO:
741 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
742 if (ftruncate(f->fd,
743 info->end_of_file_info.in.size) == -1) {
744 return map_nt_error_from_unix_common(errno);
746 break;
747 case RAW_SFILEINFO_SETATTRE:
748 unix_times.actime = info->setattre.in.access_time;
749 unix_times.modtime = info->setattre.in.write_time;
751 if (unix_times.actime == 0 && unix_times.modtime == 0) {
752 break;
755 /* set modify time = to access time if modify time was 0 */
756 if (unix_times.actime != 0 && unix_times.modtime == 0) {
757 unix_times.modtime = unix_times.actime;
760 /* Set the date on this file */
761 if (svfs_file_utime(f->fd, &unix_times) != 0) {
762 return NT_STATUS_ACCESS_DENIED;
764 break;
765 default:
766 DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
767 info->generic.level));
768 return NT_STATUS_NOT_IMPLEMENTED;
770 return NT_STATUS_OK;
775 return filesystem space info
777 static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
778 struct ntvfs_request *req, union smb_fsinfo *fs)
780 struct svfs_private *p = ntvfs->private_data;
781 struct stat st;
783 if (fs->generic.level != RAW_QFS_GENERIC) {
784 return ntvfs_map_fsinfo(ntvfs, req, fs);
787 if (sys_fsusage(p->connectpath,
788 &fs->generic.out.blocks_free,
789 &fs->generic.out.blocks_total) == -1) {
790 return map_nt_error_from_unix_common(errno);
793 fs->generic.out.block_size = 512;
795 if (stat(p->connectpath, &st) != 0) {
796 return NT_STATUS_DISK_CORRUPT_ERROR;
799 fs->generic.out.fs_id = st.st_ino;
800 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
801 fs->generic.out.serial_number = st.st_ino;
802 fs->generic.out.fs_attr = 0;
803 fs->generic.out.max_file_component_length = 255;
804 fs->generic.out.device_type = 0;
805 fs->generic.out.device_characteristics = 0;
806 fs->generic.out.quota_soft = 0;
807 fs->generic.out.quota_hard = 0;
808 fs->generic.out.quota_flags = 0;
809 fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
810 fs->generic.out.fs_type = ntvfs->ctx->fs_type;
812 return NT_STATUS_OK;
815 #if 0
817 return filesystem attribute info
819 static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
820 struct ntvfs_request *req, union smb_fsattr *fs)
822 struct stat st;
823 struct svfs_private *p = ntvfs->private_data;
825 if (fs->generic.level != RAW_FSATTR_GENERIC) {
826 return ntvfs_map_fsattr(ntvfs, req, fs);
829 if (stat(p->connectpath, &st) == -1) {
830 return map_nt_error_from_unix_common(errno);
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));
844 return NT_STATUS_OK;
846 #endif
849 return print queue info
851 static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
852 struct ntvfs_request *req, union smb_lpq *lpq)
854 return NT_STATUS_NOT_SUPPORTED;
858 list files in a directory matching a wildcard pattern
860 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 *))
865 struct svfs_dir *dir;
866 int i;
867 struct svfs_private *p = ntvfs->private_data;
868 struct search_state *search;
869 union smb_search_data file;
870 unsigned int max_count;
872 if (io->generic.level != RAW_SEARCH_TRANS2) {
873 return NT_STATUS_NOT_SUPPORTED;
876 if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
877 return NT_STATUS_NOT_SUPPORTED;
880 search = talloc_zero(p, struct search_state);
881 if (!search) {
882 return NT_STATUS_NO_MEMORY;
885 max_count = io->t2ffirst.in.max_count;
887 dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
888 if (!dir) {
889 return NT_STATUS_FOOBAR;
892 search->handle = p->next_search_handle;
893 search->dir = dir;
895 if (dir->count < max_count) {
896 max_count = dir->count;
899 for (i=0; i < max_count;i++) {
900 ZERO_STRUCT(file);
901 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
902 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
903 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
904 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
905 file.both_directory_info.name.s = dir->files[i].name;
906 file.both_directory_info.short_name.s = dir->files[i].name;
907 file.both_directory_info.size = dir->files[i].st.st_size;
908 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
910 if (!callback(search_private, &file)) {
911 break;
915 search->current_index = i;
917 io->t2ffirst.out.count = i;
918 io->t2ffirst.out.handle = search->handle;
919 io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
921 /* work out if we are going to keep the search state */
922 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
923 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
924 talloc_free(search);
925 } else {
926 p->next_search_handle++;
927 DLIST_ADD(p->search, search);
930 return NT_STATUS_OK;
933 /* continue a search */
934 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 *))
939 struct svfs_dir *dir;
940 int i;
941 struct svfs_private *p = ntvfs->private_data;
942 struct search_state *search;
943 union smb_search_data file;
944 unsigned int max_count;
946 if (io->generic.level != RAW_SEARCH_TRANS2) {
947 return NT_STATUS_NOT_SUPPORTED;
950 if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
951 return NT_STATUS_NOT_SUPPORTED;
954 for (search=p->search; search; search = search->next) {
955 if (search->handle == io->t2fnext.in.handle) break;
958 if (!search) {
959 /* we didn't find the search handle */
960 return NT_STATUS_FOOBAR;
963 dir = search->dir;
965 /* the client might be asking for something other than just continuing
966 with the search */
967 if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
968 (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
969 io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
970 /* look backwards first */
971 for (i=search->current_index; i > 0; i--) {
972 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
973 search->current_index = i;
974 goto found;
978 /* then look forwards */
979 for (i=search->current_index+1; i <= dir->count; i++) {
980 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
981 search->current_index = i;
982 goto found;
987 found:
988 max_count = search->current_index + io->t2fnext.in.max_count;
990 if (max_count > dir->count) {
991 max_count = dir->count;
994 for (i = search->current_index; i < max_count;i++) {
995 ZERO_STRUCT(file);
996 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
997 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
998 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
999 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
1000 file.both_directory_info.name.s = dir->files[i].name;
1001 file.both_directory_info.short_name.s = dir->files[i].name;
1002 file.both_directory_info.size = dir->files[i].st.st_size;
1003 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
1005 if (!callback(search_private, &file)) {
1006 break;
1010 io->t2fnext.out.count = i - search->current_index;
1011 io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
1013 search->current_index = i;
1015 /* work out if we are going to keep the search state */
1016 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
1017 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
1018 DLIST_REMOVE(p->search, search);
1019 talloc_free(search);
1022 return NT_STATUS_OK;
1025 /* close a search */
1026 static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
1027 struct ntvfs_request *req, union smb_search_close *io)
1029 struct svfs_private *p = ntvfs->private_data;
1030 struct search_state *search;
1032 for (search=p->search; search; search = search->next) {
1033 if (search->handle == io->findclose.in.handle) break;
1036 if (!search) {
1037 /* we didn't find the search handle */
1038 return NT_STATUS_FOOBAR;
1041 DLIST_REMOVE(p->search, search);
1042 talloc_free(search);
1044 return NT_STATUS_OK;
1047 /* SMBtrans - not used on file shares */
1048 static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
1049 struct ntvfs_request *req, struct smb_trans2 *trans2)
1051 return NT_STATUS_ACCESS_DENIED;
1056 initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
1058 NTSTATUS ntvfs_simple_init(TALLOC_CTX *ctx)
1060 NTSTATUS ret;
1061 struct ntvfs_ops ops;
1062 NTVFS_CURRENT_CRITICAL_SIZES(vers);
1064 ZERO_STRUCT(ops);
1066 /* fill in all the operations */
1067 ops.connect_fn = svfs_connect;
1068 ops.disconnect_fn = svfs_disconnect;
1069 ops.unlink_fn = svfs_unlink;
1070 ops.chkpath_fn = svfs_chkpath;
1071 ops.qpathinfo_fn = svfs_qpathinfo;
1072 ops.setpathinfo_fn = svfs_setpathinfo;
1073 ops.open_fn = svfs_open;
1074 ops.mkdir_fn = svfs_mkdir;
1075 ops.rmdir_fn = svfs_rmdir;
1076 ops.rename_fn = svfs_rename;
1077 ops.copy_fn = svfs_copy;
1078 ops.ioctl_fn = svfs_ioctl;
1079 ops.read_fn = svfs_read;
1080 ops.write_fn = svfs_write;
1081 ops.seek_fn = svfs_seek;
1082 ops.flush_fn = svfs_flush;
1083 ops.close_fn = svfs_close;
1084 ops.exit_fn = svfs_exit;
1085 ops.lock_fn = svfs_lock;
1086 ops.setfileinfo_fn = svfs_setfileinfo;
1087 ops.qfileinfo_fn = svfs_qfileinfo;
1088 ops.fsinfo_fn = svfs_fsinfo;
1089 ops.lpq_fn = svfs_lpq;
1090 ops.search_first_fn = svfs_search_first;
1091 ops.search_next_fn = svfs_search_next;
1092 ops.search_close_fn = svfs_search_close;
1093 ops.trans_fn = svfs_trans;
1094 ops.logoff_fn = svfs_logoff;
1095 ops.async_setup_fn = svfs_async_setup;
1096 ops.cancel_fn = svfs_cancel;
1098 /* register ourselves with the NTVFS subsystem. We register
1099 under names 'simple'
1102 ops.type = NTVFS_DISK;
1103 ops.name = "simple";
1104 ret = ntvfs_register(&ops, &vers);
1106 if (!NT_STATUS_IS_OK(ret)) {
1107 DEBUG(0,("Failed to register simple backend with name: %s!\n",
1108 ops.name));
1111 return ret;