kcc: Use correct parent in kruskal algorithm
[Samba.git] / source4 / ntvfs / simple / vfs_simple.c
blob38ecdfe66b31f049d1cf4143e4aba562a0df4443
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;
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_common(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_common(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_common(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_common(errno);
397 break;
401 do_open:
402 fd = open(unix_path, flags, 0644);
403 if (fd == -1) {
404 return map_nt_error_from_unix_common(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_common(errno);
413 status = ntvfs_handle_new(ntvfs, req, &handle);
414 NT_STATUS_NOT_OK_RETURN(status);
416 f = talloc(handle, struct svfs_file);
417 if (f == NULL) {
418 close(fd);
419 return NT_STATUS_NO_MEMORY;
421 f->fd = fd;
422 f->name = talloc_strdup(f, unix_path);
423 NT_STATUS_HAVE_NO_MEMORY(f->name);
425 DLIST_ADD(p->open_files, f);
427 status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
428 NT_STATUS_NOT_OK_RETURN(status);
430 ZERO_STRUCT(io->generic.out);
432 unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
433 unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
434 unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
435 unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
436 io->generic.out.file.ntvfs = handle;
437 io->generic.out.alloc_size = st.st_size;
438 io->generic.out.size = st.st_size;
439 io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
440 io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
442 return NT_STATUS_OK;
446 create a directory
448 static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
449 struct ntvfs_request *req, union smb_mkdir *md)
451 char *unix_path;
453 CHECK_READ_ONLY(req);
455 if (md->generic.level != RAW_MKDIR_MKDIR) {
456 return NT_STATUS_INVALID_LEVEL;
459 unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
461 if (mkdir(unix_path, 0777) == -1) {
462 return map_nt_error_from_unix_common(errno);
465 return NT_STATUS_OK;
469 remove a directory
471 static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
472 struct ntvfs_request *req, struct smb_rmdir *rd)
474 char *unix_path;
476 CHECK_READ_ONLY(req);
478 unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
480 if (rmdir(unix_path) == -1) {
481 return map_nt_error_from_unix_common(errno);
484 return NT_STATUS_OK;
488 rename a set of files
490 static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
491 struct ntvfs_request *req, union smb_rename *ren)
493 char *unix_path1, *unix_path2;
495 CHECK_READ_ONLY(req);
497 if (ren->generic.level != RAW_RENAME_RENAME) {
498 return NT_STATUS_INVALID_LEVEL;
501 unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
502 unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
504 if (rename(unix_path1, unix_path2) == -1) {
505 return map_nt_error_from_unix_common(errno);
508 return NT_STATUS_OK;
512 copy a set of files
514 static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
515 struct ntvfs_request *req, struct smb_copy *cp)
517 return NT_STATUS_NOT_SUPPORTED;
521 read from a file
523 static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
524 struct ntvfs_request *req, union smb_read *rd)
526 struct svfs_private *p = ntvfs->private_data;
527 struct svfs_file *f;
528 ssize_t ret;
530 if (rd->generic.level != RAW_READ_READX) {
531 return NT_STATUS_NOT_SUPPORTED;
534 f = find_fd(p, rd->readx.in.file.ntvfs);
535 if (!f) {
536 return NT_STATUS_INVALID_HANDLE;
539 ret = pread(f->fd,
540 rd->readx.out.data,
541 rd->readx.in.maxcnt,
542 rd->readx.in.offset);
543 if (ret == -1) {
544 return map_nt_error_from_unix_common(errno);
547 rd->readx.out.nread = ret;
548 rd->readx.out.remaining = 0; /* should fill this in? */
549 rd->readx.out.compaction_mode = 0;
551 return NT_STATUS_OK;
555 write to a file
557 static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
558 struct ntvfs_request *req, union smb_write *wr)
560 struct svfs_private *p = ntvfs->private_data;
561 struct svfs_file *f;
562 ssize_t ret;
564 if (wr->generic.level != RAW_WRITE_WRITEX) {
565 return ntvfs_map_write(ntvfs, req, wr);
568 CHECK_READ_ONLY(req);
570 f = find_fd(p, wr->writex.in.file.ntvfs);
571 if (!f) {
572 return NT_STATUS_INVALID_HANDLE;
575 ret = pwrite(f->fd,
576 wr->writex.in.data,
577 wr->writex.in.count,
578 wr->writex.in.offset);
579 if (ret == -1) {
580 return map_nt_error_from_unix_common(errno);
583 wr->writex.out.nwritten = ret;
584 wr->writex.out.remaining = 0; /* should fill this in? */
586 return NT_STATUS_OK;
590 seek in a file
592 static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
593 struct ntvfs_request *req,
594 union smb_seek *io)
596 return NT_STATUS_NOT_SUPPORTED;
600 flush a file
602 static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
603 struct ntvfs_request *req,
604 union smb_flush *io)
606 struct svfs_private *p = ntvfs->private_data;
607 struct svfs_file *f;
609 switch (io->generic.level) {
610 case RAW_FLUSH_FLUSH:
611 case RAW_FLUSH_SMB2:
612 /* ignore the additional unknown option in SMB2 */
613 f = find_fd(p, io->generic.in.file.ntvfs);
614 if (!f) {
615 return NT_STATUS_INVALID_HANDLE;
617 fsync(f->fd);
618 return NT_STATUS_OK;
620 case RAW_FLUSH_ALL:
621 for (f=p->open_files;f;f=f->next) {
622 fsync(f->fd);
624 return NT_STATUS_OK;
627 return NT_STATUS_INVALID_LEVEL;
631 close a file
633 static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
634 struct ntvfs_request *req,
635 union smb_close *io)
637 struct svfs_private *p = ntvfs->private_data;
638 struct svfs_file *f;
640 if (io->generic.level != RAW_CLOSE_CLOSE) {
641 /* we need a mapping function */
642 return NT_STATUS_INVALID_LEVEL;
645 f = find_fd(p, io->close.in.file.ntvfs);
646 if (!f) {
647 return NT_STATUS_INVALID_HANDLE;
650 if (close(f->fd) == -1) {
651 return map_nt_error_from_unix_common(errno);
654 DLIST_REMOVE(p->open_files, f);
655 talloc_free(f->name);
656 talloc_free(f);
658 return NT_STATUS_OK;
662 exit - closing files
664 static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
665 struct ntvfs_request *req)
667 return NT_STATUS_NOT_SUPPORTED;
671 logoff - closing files
673 static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
674 struct ntvfs_request *req)
676 return NT_STATUS_NOT_SUPPORTED;
680 setup for an async call
682 static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
683 struct ntvfs_request *req,
684 void *private_data)
686 return NT_STATUS_OK;
690 cancel an async call
692 static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
694 return NT_STATUS_UNSUCCESSFUL;
698 lock a byte range
700 static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
701 struct ntvfs_request *req, union smb_lock *lck)
703 DEBUG(0,("REWRITE: not doing byte range locking!\n"));
704 return NT_STATUS_OK;
708 set info on a pathname
710 static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
711 struct ntvfs_request *req, union smb_setfileinfo *st)
713 CHECK_READ_ONLY(req);
715 return NT_STATUS_NOT_SUPPORTED;
719 set info on a open file
721 static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
722 struct ntvfs_request *req,
723 union smb_setfileinfo *info)
725 struct svfs_private *p = ntvfs->private_data;
726 struct svfs_file *f;
727 struct utimbuf unix_times;
729 CHECK_READ_ONLY(req);
731 f = find_fd(p, info->generic.in.file.ntvfs);
732 if (!f) {
733 return NT_STATUS_INVALID_HANDLE;
736 switch (info->generic.level) {
737 case RAW_SFILEINFO_END_OF_FILE_INFO:
738 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
739 if (ftruncate(f->fd,
740 info->end_of_file_info.in.size) == -1) {
741 return map_nt_error_from_unix_common(errno);
743 break;
744 case RAW_SFILEINFO_SETATTRE:
745 unix_times.actime = info->setattre.in.access_time;
746 unix_times.modtime = info->setattre.in.write_time;
748 if (unix_times.actime == 0 && unix_times.modtime == 0) {
749 break;
752 /* set modify time = to access time if modify time was 0 */
753 if (unix_times.actime != 0 && unix_times.modtime == 0) {
754 unix_times.modtime = unix_times.actime;
757 /* Set the date on this file */
758 if (svfs_file_utime(f->fd, &unix_times) != 0) {
759 return NT_STATUS_ACCESS_DENIED;
761 break;
762 default:
763 DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
764 info->generic.level));
765 return NT_STATUS_NOT_IMPLEMENTED;
767 return NT_STATUS_OK;
772 return filesystem space info
774 static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
775 struct ntvfs_request *req, union smb_fsinfo *fs)
777 struct svfs_private *p = ntvfs->private_data;
778 struct stat st;
780 if (fs->generic.level != RAW_QFS_GENERIC) {
781 return ntvfs_map_fsinfo(ntvfs, req, fs);
784 if (sys_fsusage(p->connectpath,
785 &fs->generic.out.blocks_free,
786 &fs->generic.out.blocks_total) == -1) {
787 return map_nt_error_from_unix_common(errno);
790 fs->generic.out.block_size = 512;
792 if (stat(p->connectpath, &st) != 0) {
793 return NT_STATUS_DISK_CORRUPT_ERROR;
796 fs->generic.out.fs_id = st.st_ino;
797 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
798 fs->generic.out.serial_number = st.st_ino;
799 fs->generic.out.fs_attr = 0;
800 fs->generic.out.max_file_component_length = 255;
801 fs->generic.out.device_type = 0;
802 fs->generic.out.device_characteristics = 0;
803 fs->generic.out.quota_soft = 0;
804 fs->generic.out.quota_hard = 0;
805 fs->generic.out.quota_flags = 0;
806 fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
807 fs->generic.out.fs_type = ntvfs->ctx->fs_type;
809 return NT_STATUS_OK;
812 #if 0
814 return filesystem attribute info
816 static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
817 struct ntvfs_request *req, union smb_fsattr *fs)
819 struct stat st;
820 struct svfs_private *p = ntvfs->private_data;
822 if (fs->generic.level != RAW_FSATTR_GENERIC) {
823 return ntvfs_map_fsattr(ntvfs, req, fs);
826 if (stat(p->connectpath, &st) == -1) {
827 return map_nt_error_from_unix_common(errno);
830 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
831 fs->generic.out.fs_attr =
832 FILE_CASE_PRESERVED_NAMES |
833 FILE_CASE_SENSITIVE_SEARCH |
834 FILE_PERSISTENT_ACLS;
835 fs->generic.out.max_file_component_length = 255;
836 fs->generic.out.serial_number = 1;
837 fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
838 fs->generic.out.volume_name = talloc_strdup(req,
839 lpcfg_servicename(req->tcon->service));
841 return NT_STATUS_OK;
843 #endif
846 return print queue info
848 static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
849 struct ntvfs_request *req, union smb_lpq *lpq)
851 return NT_STATUS_NOT_SUPPORTED;
855 list files in a directory matching a wildcard pattern
857 static NTSTATUS svfs_search_first(struct ntvfs_module_context *ntvfs,
858 struct ntvfs_request *req, union smb_search_first *io,
859 void *search_private,
860 bool (*callback)(void *, const union smb_search_data *))
862 struct svfs_dir *dir;
863 int i;
864 struct svfs_private *p = ntvfs->private_data;
865 struct search_state *search;
866 union smb_search_data file;
867 unsigned int max_count;
869 if (io->generic.level != RAW_SEARCH_TRANS2) {
870 return NT_STATUS_NOT_SUPPORTED;
873 if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
874 return NT_STATUS_NOT_SUPPORTED;
877 search = talloc_zero(p, struct search_state);
878 if (!search) {
879 return NT_STATUS_NO_MEMORY;
882 max_count = io->t2ffirst.in.max_count;
884 dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
885 if (!dir) {
886 return NT_STATUS_FOOBAR;
889 search->handle = p->next_search_handle;
890 search->dir = dir;
892 if (dir->count < max_count) {
893 max_count = dir->count;
896 for (i=0; i < max_count;i++) {
897 ZERO_STRUCT(file);
898 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
899 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
900 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
901 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
902 file.both_directory_info.name.s = dir->files[i].name;
903 file.both_directory_info.short_name.s = dir->files[i].name;
904 file.both_directory_info.size = dir->files[i].st.st_size;
905 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
907 if (!callback(search_private, &file)) {
908 break;
912 search->current_index = i;
914 io->t2ffirst.out.count = i;
915 io->t2ffirst.out.handle = search->handle;
916 io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
918 /* work out if we are going to keep the search state */
919 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
920 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
921 talloc_free(search);
922 } else {
923 p->next_search_handle++;
924 DLIST_ADD(p->search, search);
927 return NT_STATUS_OK;
930 /* continue a search */
931 static NTSTATUS svfs_search_next(struct ntvfs_module_context *ntvfs,
932 struct ntvfs_request *req, union smb_search_next *io,
933 void *search_private,
934 bool (*callback)(void *, const union smb_search_data *))
936 struct svfs_dir *dir;
937 int i;
938 struct svfs_private *p = ntvfs->private_data;
939 struct search_state *search;
940 union smb_search_data file;
941 unsigned int max_count;
943 if (io->generic.level != RAW_SEARCH_TRANS2) {
944 return NT_STATUS_NOT_SUPPORTED;
947 if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
948 return NT_STATUS_NOT_SUPPORTED;
951 for (search=p->search; search; search = search->next) {
952 if (search->handle == io->t2fnext.in.handle) break;
955 if (!search) {
956 /* we didn't find the search handle */
957 return NT_STATUS_FOOBAR;
960 dir = search->dir;
962 /* the client might be asking for something other than just continuing
963 with the search */
964 if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
965 (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
966 io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
967 /* look backwards first */
968 for (i=search->current_index; i > 0; i--) {
969 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
970 search->current_index = i;
971 goto found;
975 /* then look forwards */
976 for (i=search->current_index+1; i <= dir->count; i++) {
977 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
978 search->current_index = i;
979 goto found;
984 found:
985 max_count = search->current_index + io->t2fnext.in.max_count;
987 if (max_count > dir->count) {
988 max_count = dir->count;
991 for (i = search->current_index; i < max_count;i++) {
992 ZERO_STRUCT(file);
993 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
994 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
995 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
996 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
997 file.both_directory_info.name.s = dir->files[i].name;
998 file.both_directory_info.short_name.s = dir->files[i].name;
999 file.both_directory_info.size = dir->files[i].st.st_size;
1000 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
1002 if (!callback(search_private, &file)) {
1003 break;
1007 io->t2fnext.out.count = i - search->current_index;
1008 io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
1010 search->current_index = i;
1012 /* work out if we are going to keep the search state */
1013 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
1014 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
1015 DLIST_REMOVE(p->search, search);
1016 talloc_free(search);
1019 return NT_STATUS_OK;
1022 /* close a search */
1023 static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
1024 struct ntvfs_request *req, union smb_search_close *io)
1026 struct svfs_private *p = ntvfs->private_data;
1027 struct search_state *search;
1029 for (search=p->search; search; search = search->next) {
1030 if (search->handle == io->findclose.in.handle) break;
1033 if (!search) {
1034 /* we didn't find the search handle */
1035 return NT_STATUS_FOOBAR;
1038 DLIST_REMOVE(p->search, search);
1039 talloc_free(search);
1041 return NT_STATUS_OK;
1044 /* SMBtrans - not used on file shares */
1045 static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
1046 struct ntvfs_request *req, struct smb_trans2 *trans2)
1048 return NT_STATUS_ACCESS_DENIED;
1053 initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
1055 NTSTATUS ntvfs_simple_init(void)
1057 NTSTATUS ret;
1058 struct ntvfs_ops ops;
1059 NTVFS_CURRENT_CRITICAL_SIZES(vers);
1061 ZERO_STRUCT(ops);
1063 /* fill in all the operations */
1064 ops.connect_fn = svfs_connect;
1065 ops.disconnect_fn = svfs_disconnect;
1066 ops.unlink_fn = svfs_unlink;
1067 ops.chkpath_fn = svfs_chkpath;
1068 ops.qpathinfo_fn = svfs_qpathinfo;
1069 ops.setpathinfo_fn = svfs_setpathinfo;
1070 ops.open_fn = svfs_open;
1071 ops.mkdir_fn = svfs_mkdir;
1072 ops.rmdir_fn = svfs_rmdir;
1073 ops.rename_fn = svfs_rename;
1074 ops.copy_fn = svfs_copy;
1075 ops.ioctl_fn = svfs_ioctl;
1076 ops.read_fn = svfs_read;
1077 ops.write_fn = svfs_write;
1078 ops.seek_fn = svfs_seek;
1079 ops.flush_fn = svfs_flush;
1080 ops.close_fn = svfs_close;
1081 ops.exit_fn = svfs_exit;
1082 ops.lock_fn = svfs_lock;
1083 ops.setfileinfo_fn = svfs_setfileinfo;
1084 ops.qfileinfo_fn = svfs_qfileinfo;
1085 ops.fsinfo_fn = svfs_fsinfo;
1086 ops.lpq_fn = svfs_lpq;
1087 ops.search_first_fn = svfs_search_first;
1088 ops.search_next_fn = svfs_search_next;
1089 ops.search_close_fn = svfs_search_close;
1090 ops.trans_fn = svfs_trans;
1091 ops.logoff_fn = svfs_logoff;
1092 ops.async_setup_fn = svfs_async_setup;
1093 ops.cancel_fn = svfs_cancel;
1095 /* register ourselves with the NTVFS subsystem. We register
1096 under names 'simple'
1099 ops.type = NTVFS_DISK;
1100 ops.name = "simple";
1101 ret = ntvfs_register(&ops, &vers);
1103 if (!NT_STATUS_IS_OK(ret)) {
1104 DEBUG(0,("Failed to register simple backend with name: %s!\n",
1105 ops.name));
1108 return ret;