r15185: Force all NTVFS modules to provide a critical sizes structure so
[Samba.git] / source / ntvfs / simple / vfs_simple.c
blob35c18e3f1c52510403a2654827c07b99bf289797
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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 this implements a very simple NTVFS filesystem backend.
25 this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
26 minimal work to give a working backend.
29 #include "includes.h"
30 #include "system/dir.h"
31 #include "system/filesys.h"
32 #include "svfs.h"
33 #include "system/time.h"
34 #include "dlinklist.h"
35 #include "ntvfs/ntvfs.h"
36 #include "ntvfs/simple/proto.h"
38 #ifndef O_DIRECTORY
39 #define O_DIRECTORY 0
40 #endif
42 #define CHECK_READ_ONLY(req) do { if (lp_readonly(ntvfs->ctx->config.snum)) return NT_STATUS_ACCESS_DENIED; } while (0)
45 connect to a share - used when a tree_connect operation comes
46 in. For a disk based backend we needs to ensure that the base
47 directory exists (tho it doesn't need to be accessible by the user,
48 that comes later)
50 static NTSTATUS svfs_connect(struct ntvfs_module_context *ntvfs,
51 struct ntvfs_request *req, const char *sharename)
53 struct stat st;
54 struct svfs_private *private;
55 int snum = ntvfs->ctx->config.snum;
57 private = talloc(ntvfs, struct svfs_private);
59 private->next_search_handle = 0;
60 private->connectpath = talloc_strdup(private, lp_pathname(snum));
61 private->open_files = NULL;
62 private->search = NULL;
64 /* the directory must exist */
65 if (stat(private->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
66 DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n",
67 private->connectpath, sharename));
68 return NT_STATUS_BAD_NETWORK_NAME;
71 ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
72 NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
73 ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
74 NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
76 ntvfs->private_data = private;
78 DEBUG(0,("WARNING: ntvfs simple: connect to share [%s] with ROOT privileges!!!\n",sharename));
80 return NT_STATUS_OK;
84 disconnect from a share
86 static NTSTATUS svfs_disconnect(struct ntvfs_module_context *ntvfs)
88 return NT_STATUS_OK;
92 find open file handle given fd
94 static struct svfs_file *find_fd(struct svfs_private *private, int fd)
96 struct svfs_file *f;
97 for (f=private->open_files;f;f=f->next) {
98 if (f->fd == fd) {
99 return f;
102 return NULL;
106 delete a file - the dirtype specifies the file types to include in the search.
107 The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
109 static NTSTATUS svfs_unlink(struct ntvfs_module_context *ntvfs,
110 struct ntvfs_request *req,
111 union smb_unlink *unl)
113 char *unix_path;
115 CHECK_READ_ONLY(req);
117 unix_path = svfs_unix_path(ntvfs, req, unl->unlink.in.pattern);
119 /* ignoring wildcards ... */
120 if (unlink(unix_path) == -1) {
121 return map_nt_error_from_unix(errno);
124 return NT_STATUS_OK;
129 ioctl interface - we don't do any
131 static NTSTATUS svfs_ioctl(struct ntvfs_module_context *ntvfs,
132 struct ntvfs_request *req, union smb_ioctl *io)
134 return NT_STATUS_INVALID_PARAMETER;
138 check if a directory exists
140 static NTSTATUS svfs_chkpath(struct ntvfs_module_context *ntvfs,
141 struct ntvfs_request *req,
142 union smb_chkpath *cp)
144 char *unix_path;
145 struct stat st;
147 unix_path = svfs_unix_path(ntvfs, req, cp->chkpath.in.path);
149 if (stat(unix_path, &st) == -1) {
150 return map_nt_error_from_unix(errno);
153 if (!S_ISDIR(st.st_mode)) {
154 return NT_STATUS_NOT_A_DIRECTORY;
157 return NT_STATUS_OK;
161 build a file_id from a stat struct
163 static uint64_t svfs_file_id(struct stat *st)
165 uint64_t ret = st->st_ino;
166 ret <<= 32;
167 ret |= st->st_dev;
168 return ret;
172 approximately map a struct stat to a generic fileinfo struct
174 static NTSTATUS svfs_map_fileinfo(struct ntvfs_module_context *ntvfs,
175 struct ntvfs_request *req, union smb_fileinfo *info,
176 struct stat *st, const char *unix_path)
178 struct svfs_dir *dir = NULL;
179 char *pattern = NULL;
180 int i;
181 const char *s, *short_name;
183 s = strrchr(unix_path, '/');
184 if (s) {
185 short_name = s+1;
186 } else {
187 short_name = "";
190 asprintf(&pattern, "%s:*", unix_path);
192 if (pattern) {
193 dir = svfs_list_unix(req, req, pattern);
196 unix_to_nt_time(&info->generic.out.create_time, st->st_ctime);
197 unix_to_nt_time(&info->generic.out.access_time, st->st_atime);
198 unix_to_nt_time(&info->generic.out.write_time, st->st_mtime);
199 unix_to_nt_time(&info->generic.out.change_time, st->st_mtime);
200 info->generic.out.alloc_size = st->st_size;
201 info->generic.out.size = st->st_size;
202 info->generic.out.attrib = svfs_unix_to_dos_attrib(st->st_mode);
203 info->generic.out.alloc_size = st->st_blksize * st->st_blocks;
204 info->generic.out.nlink = st->st_nlink;
205 info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
206 info->generic.out.file_id = svfs_file_id(st);
207 /* REWRITE: TODO stuff in here */
208 info->generic.out.delete_pending = 0;
209 info->generic.out.ea_size = 0;
210 info->generic.out.num_eas = 0;
211 info->generic.out.fname.s = talloc_strdup(req, short_name);
212 info->generic.out.alt_fname.s = talloc_strdup(req, short_name);
213 info->generic.out.compressed_size = 0;
214 info->generic.out.format = 0;
215 info->generic.out.unit_shift = 0;
216 info->generic.out.chunk_shift = 0;
217 info->generic.out.cluster_shift = 0;
219 info->generic.out.access_flags = 0;
220 info->generic.out.position = 0;
221 info->generic.out.mode = 0;
222 info->generic.out.alignment_requirement = 0;
223 info->generic.out.reparse_tag = 0;
224 info->generic.out.num_streams = 0;
225 /* setup a single data stream */
226 info->generic.out.num_streams = 1 + (dir?dir->count:0);
227 info->generic.out.streams = talloc_array(req,
228 struct stream_struct,
229 info->generic.out.num_streams);
230 if (!info->generic.out.streams) {
231 return NT_STATUS_NO_MEMORY;
233 info->generic.out.streams[0].size = st->st_size;
234 info->generic.out.streams[0].alloc_size = st->st_size;
235 info->generic.out.streams[0].stream_name.s = talloc_strdup(req,"::$DATA");
237 for (i=0;dir && i<dir->count;i++) {
238 s = strchr(dir->files[i].name, ':');
239 info->generic.out.streams[1+i].size = dir->files[i].st.st_size;
240 info->generic.out.streams[1+i].alloc_size = dir->files[i].st.st_size;
241 info->generic.out.streams[1+i].stream_name.s = s?s:dir->files[i].name;
244 return NT_STATUS_OK;
248 return info on a pathname
250 static NTSTATUS svfs_qpathinfo(struct ntvfs_module_context *ntvfs,
251 struct ntvfs_request *req, union smb_fileinfo *info)
253 char *unix_path;
254 struct stat st;
256 DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.file.path, info->generic.level));
257 if (info->generic.level != RAW_FILEINFO_GENERIC) {
258 return ntvfs_map_qpathinfo(ntvfs, req, info);
261 unix_path = svfs_unix_path(ntvfs, req, info->generic.in.file.path);
262 DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path));
263 if (stat(unix_path, &st) == -1) {
264 DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno));
265 return map_nt_error_from_unix(errno);
267 DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path));
268 return svfs_map_fileinfo(ntvfs, req, info, &st, unix_path);
272 query info on a open file
274 static NTSTATUS svfs_qfileinfo(struct ntvfs_module_context *ntvfs,
275 struct ntvfs_request *req, union smb_fileinfo *info)
277 struct svfs_private *private = ntvfs->private_data;
278 struct svfs_file *f;
279 struct stat st;
281 if (info->generic.level != RAW_FILEINFO_GENERIC) {
282 return ntvfs_map_qfileinfo(ntvfs, req, info);
285 f = find_fd(private, info->generic.in.file.fnum);
286 if (!f) {
287 return NT_STATUS_INVALID_HANDLE;
290 if (fstat(info->generic.in.file.fnum, &st) == -1) {
291 return map_nt_error_from_unix(errno);
294 return svfs_map_fileinfo(ntvfs, req,info, &st, f->name);
299 open a file
301 static NTSTATUS svfs_open(struct ntvfs_module_context *ntvfs,
302 struct ntvfs_request *req, union smb_open *io)
304 struct svfs_private *private = ntvfs->private_data;
305 char *unix_path;
306 struct stat st;
307 int fd, flags;
308 struct svfs_file *f;
309 int create_flags, rdwr_flags;
310 BOOL readonly;
312 if (io->generic.level != RAW_OPEN_GENERIC) {
313 return ntvfs_map_open(ntvfs, req, io);
316 readonly = lp_readonly(ntvfs->ctx->config.snum);
317 if (readonly) {
318 create_flags = 0;
319 rdwr_flags = O_RDONLY;
320 } else {
321 create_flags = O_CREAT;
322 rdwr_flags = O_RDWR;
325 unix_path = svfs_unix_path(ntvfs, req, io->ntcreatex.in.fname);
327 switch (io->generic.in.open_disposition) {
328 case NTCREATEX_DISP_SUPERSEDE:
329 case NTCREATEX_DISP_OVERWRITE_IF:
330 flags = create_flags | O_TRUNC;
331 break;
332 case NTCREATEX_DISP_OPEN:
333 case NTCREATEX_DISP_OVERWRITE:
334 flags = 0;
335 break;
336 case NTCREATEX_DISP_CREATE:
337 flags = create_flags | O_EXCL;
338 break;
339 case NTCREATEX_DISP_OPEN_IF:
340 flags = create_flags;
341 break;
342 default:
343 flags = 0;
344 break;
347 flags |= rdwr_flags;
349 if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
350 flags = O_RDONLY | O_DIRECTORY;
351 if (readonly) {
352 goto do_open;
354 switch (io->generic.in.open_disposition) {
355 case NTCREATEX_DISP_CREATE:
356 if (mkdir(unix_path, 0755) == -1) {
357 DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
358 return map_nt_error_from_unix(errno);
360 break;
361 case NTCREATEX_DISP_OPEN_IF:
362 if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
363 DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
364 return map_nt_error_from_unix(errno);
366 break;
370 do_open:
371 fd = open(unix_path, flags, 0644);
372 if (fd == -1) {
373 return map_nt_error_from_unix(errno);
376 if (fstat(fd, &st) == -1) {
377 DEBUG(9,("svfs_open: fstat errno=%d\n", errno));
378 close(fd);
379 return map_nt_error_from_unix(errno);
382 f = talloc(ntvfs, struct svfs_file);
383 NT_STATUS_HAVE_NO_MEMORY(f);
384 f->fd = fd;
385 f->name = talloc_strdup(f, unix_path);
386 NT_STATUS_HAVE_NO_MEMORY(f->name);
388 DLIST_ADD(private->open_files, f);
390 ZERO_STRUCT(io->generic.out);
392 unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
393 unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
394 unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
395 unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
396 io->generic.out.file.fnum = fd;
397 io->generic.out.alloc_size = st.st_size;
398 io->generic.out.size = st.st_size;
399 io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
400 io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
402 return NT_STATUS_OK;
406 create a directory
408 static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
409 struct ntvfs_request *req, union smb_mkdir *md)
411 char *unix_path;
413 CHECK_READ_ONLY(req);
415 if (md->generic.level != RAW_MKDIR_MKDIR) {
416 return NT_STATUS_INVALID_LEVEL;
419 unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
421 if (mkdir(unix_path, 0777) == -1) {
422 return map_nt_error_from_unix(errno);
425 return NT_STATUS_OK;
429 remove a directory
431 static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
432 struct ntvfs_request *req, struct smb_rmdir *rd)
434 char *unix_path;
436 CHECK_READ_ONLY(req);
438 unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
440 if (rmdir(unix_path) == -1) {
441 return map_nt_error_from_unix(errno);
444 return NT_STATUS_OK;
448 rename a set of files
450 static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
451 struct ntvfs_request *req, union smb_rename *ren)
453 char *unix_path1, *unix_path2;
455 CHECK_READ_ONLY(req);
457 if (ren->generic.level != RAW_RENAME_RENAME) {
458 return NT_STATUS_INVALID_LEVEL;
461 unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
462 unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
464 if (rename(unix_path1, unix_path2) == -1) {
465 return map_nt_error_from_unix(errno);
468 return NT_STATUS_OK;
472 copy a set of files
474 static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
475 struct ntvfs_request *req, struct smb_copy *cp)
477 return NT_STATUS_NOT_SUPPORTED;
481 read from a file
483 static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
484 struct ntvfs_request *req, union smb_read *rd)
486 ssize_t ret;
488 if (rd->generic.level != RAW_READ_READX) {
489 return NT_STATUS_NOT_SUPPORTED;
492 ret = pread(rd->readx.in.file.fnum,
493 rd->readx.out.data,
494 rd->readx.in.maxcnt,
495 rd->readx.in.offset);
496 if (ret == -1) {
497 return map_nt_error_from_unix(errno);
500 rd->readx.out.nread = ret;
501 rd->readx.out.remaining = 0; /* should fill this in? */
502 rd->readx.out.compaction_mode = 0;
504 return NT_STATUS_OK;
508 write to a file
510 static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
511 struct ntvfs_request *req, union smb_write *wr)
513 ssize_t ret;
515 if (wr->generic.level != RAW_WRITE_WRITEX) {
516 return ntvfs_map_write(ntvfs, req, wr);
519 CHECK_READ_ONLY(req);
521 ret = pwrite(wr->writex.in.file.fnum,
522 wr->writex.in.data,
523 wr->writex.in.count,
524 wr->writex.in.offset);
525 if (ret == -1) {
526 return map_nt_error_from_unix(errno);
529 wr->writex.out.nwritten = ret;
530 wr->writex.out.remaining = 0; /* should fill this in? */
532 return NT_STATUS_OK;
536 seek in a file
538 static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
539 struct ntvfs_request *req,
540 union smb_seek *io)
542 return NT_STATUS_NOT_SUPPORTED;
546 flush a file
548 static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
549 struct ntvfs_request *req,
550 union smb_flush *io)
552 fsync(io->flush.in.file.fnum);
553 return NT_STATUS_OK;
557 close a file
559 static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
560 struct ntvfs_request *req,
561 union smb_close *io)
563 struct svfs_private *private = ntvfs->private_data;
564 struct svfs_file *f;
566 if (io->generic.level != RAW_CLOSE_CLOSE) {
567 /* we need a mapping function */
568 return NT_STATUS_INVALID_LEVEL;
571 f = find_fd(private, io->close.in.file.fnum);
572 if (!f) {
573 return NT_STATUS_INVALID_HANDLE;
576 if (close(io->close.in.file.fnum) == -1) {
577 return map_nt_error_from_unix(errno);
580 DLIST_REMOVE(private->open_files, f);
581 talloc_free(f->name);
582 talloc_free(f);
584 return NT_STATUS_OK;
588 exit - closing files
590 static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
591 struct ntvfs_request *req)
593 return NT_STATUS_NOT_SUPPORTED;
597 logoff - closing files
599 static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
600 struct ntvfs_request *req)
602 return NT_STATUS_NOT_SUPPORTED;
606 setup for an async call
608 static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
609 struct ntvfs_request *req,
610 void *private)
612 return NT_STATUS_OK;
616 cancel an async call
618 static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
620 return NT_STATUS_UNSUCCESSFUL;
624 lock a byte range
626 static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
627 struct ntvfs_request *req, union smb_lock *lck)
629 DEBUG(0,("REWRITE: not doing byte range locking!\n"));
630 return NT_STATUS_OK;
634 set info on a pathname
636 static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
637 struct ntvfs_request *req, union smb_setfileinfo *st)
639 CHECK_READ_ONLY(req);
641 return NT_STATUS_NOT_SUPPORTED;
645 set info on a open file
647 static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
648 struct ntvfs_request *req,
649 union smb_setfileinfo *info)
651 struct utimbuf unix_times;
652 int fd;
654 CHECK_READ_ONLY(req);
656 switch (info->generic.level) {
657 case RAW_SFILEINFO_END_OF_FILE_INFO:
658 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
659 if (ftruncate(info->end_of_file_info.in.file.fnum,
660 info->end_of_file_info.in.size) == -1) {
661 return map_nt_error_from_unix(errno);
663 break;
664 case RAW_SFILEINFO_SETATTRE:
665 unix_times.actime = info->setattre.in.access_time;
666 unix_times.modtime = info->setattre.in.write_time;
667 fd = info->setattre.in.file.fnum;
669 if (unix_times.actime == 0 && unix_times.modtime == 0) {
670 break;
673 /* set modify time = to access time if modify time was 0 */
674 if (unix_times.actime != 0 && unix_times.modtime == 0) {
675 unix_times.modtime = unix_times.actime;
678 /* Set the date on this file */
679 if (svfs_file_utime(fd, &unix_times) != 0) {
680 return NT_STATUS_ACCESS_DENIED;
682 break;
683 default:
684 DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
685 info->generic.level));
686 return NT_STATUS_NOT_IMPLEMENTED;
688 return NT_STATUS_OK;
693 return filesystem space info
695 static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
696 struct ntvfs_request *req, union smb_fsinfo *fs)
698 struct svfs_private *private = ntvfs->private_data;
699 struct stat st;
701 if (fs->generic.level != RAW_QFS_GENERIC) {
702 return ntvfs_map_fsinfo(ntvfs, req, fs);
705 if (sys_fsusage(private->connectpath,
706 &fs->generic.out.blocks_free,
707 &fs->generic.out.blocks_total) == -1) {
708 return map_nt_error_from_unix(errno);
711 fs->generic.out.block_size = 512;
713 if (stat(private->connectpath, &st) != 0) {
714 return NT_STATUS_DISK_CORRUPT_ERROR;
717 fs->generic.out.fs_id = st.st_ino;
718 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
719 fs->generic.out.serial_number = st.st_ino;
720 fs->generic.out.fs_attr = 0;
721 fs->generic.out.max_file_component_length = 255;
722 fs->generic.out.device_type = 0;
723 fs->generic.out.device_characteristics = 0;
724 fs->generic.out.quota_soft = 0;
725 fs->generic.out.quota_hard = 0;
726 fs->generic.out.quota_flags = 0;
727 fs->generic.out.volume_name = talloc_strdup(req, lp_servicename(ntvfs->ctx->config.snum));
728 fs->generic.out.fs_type = ntvfs->ctx->fs_type;
730 return NT_STATUS_OK;
733 #if 0
735 return filesystem attribute info
737 static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
738 struct ntvfs_request *req, union smb_fsattr *fs)
740 struct stat st;
741 struct svfs_private *private = ntvfs->private_data;
743 if (fs->generic.level != RAW_FSATTR_GENERIC) {
744 return ntvfs_map_fsattr(ntvfs, req, fs);
747 if (stat(private->connectpath, &st) == -1) {
748 return map_nt_error_from_unix(errno);
751 unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
752 fs->generic.out.fs_attr =
753 FILE_CASE_PRESERVED_NAMES |
754 FILE_CASE_SENSITIVE_SEARCH |
755 FILE_PERSISTENT_ACLS;
756 fs->generic.out.max_file_component_length = 255;
757 fs->generic.out.serial_number = 1;
758 fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
759 fs->generic.out.volume_name = talloc_strdup(req,
760 lp_servicename(req->tcon->service));
762 return NT_STATUS_OK;
764 #endif
767 return print queue info
769 static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
770 struct ntvfs_request *req, union smb_lpq *lpq)
772 return NT_STATUS_NOT_SUPPORTED;
776 list files in a directory matching a wildcard pattern
778 static NTSTATUS svfs_search_first(struct ntvfs_module_context *ntvfs,
779 struct ntvfs_request *req, union smb_search_first *io,
780 void *search_private,
781 BOOL (*callback)(void *, union smb_search_data *))
783 struct svfs_dir *dir;
784 int i;
785 struct svfs_private *private = ntvfs->private_data;
786 struct search_state *search;
787 union smb_search_data file;
788 uint_t max_count;
790 if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) {
791 return NT_STATUS_NOT_SUPPORTED;
794 search = talloc_zero(private, struct search_state);
795 if (!search) {
796 return NT_STATUS_NO_MEMORY;
799 max_count = io->t2ffirst.in.max_count;
801 dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
802 if (!dir) {
803 return NT_STATUS_FOOBAR;
806 search->handle = private->next_search_handle;
807 search->dir = dir;
809 if (dir->count < max_count) {
810 max_count = dir->count;
813 for (i=0; i < max_count;i++) {
814 ZERO_STRUCT(file);
815 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
816 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
817 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
818 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
819 file.both_directory_info.name.s = dir->files[i].name;
820 file.both_directory_info.short_name.s = dir->files[i].name;
821 file.both_directory_info.size = dir->files[i].st.st_size;
822 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
824 if (!callback(search_private, &file)) {
825 break;
829 search->current_index = i;
831 io->t2ffirst.out.count = i;
832 io->t2ffirst.out.handle = search->handle;
833 io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
835 /* work out if we are going to keep the search state */
836 if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
837 ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
838 talloc_free(search);
839 } else {
840 private->next_search_handle++;
841 DLIST_ADD(private->search, search);
844 return NT_STATUS_OK;
847 /* continue a search */
848 static NTSTATUS svfs_search_next(struct ntvfs_module_context *ntvfs,
849 struct ntvfs_request *req, union smb_search_next *io,
850 void *search_private,
851 BOOL (*callback)(void *, union smb_search_data *))
853 struct svfs_dir *dir;
854 int i;
855 struct svfs_private *private = ntvfs->private_data;
856 struct search_state *search;
857 union smb_search_data file;
858 uint_t max_count;
860 if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) {
861 return NT_STATUS_NOT_SUPPORTED;
864 for (search=private->search; search; search = search->next) {
865 if (search->handle == io->t2fnext.in.handle) break;
868 if (!search) {
869 /* we didn't find the search handle */
870 return NT_STATUS_FOOBAR;
873 dir = search->dir;
875 /* the client might be asking for something other than just continuing
876 with the search */
877 if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
878 (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
879 io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
880 /* look backwards first */
881 for (i=search->current_index; i > 0; i--) {
882 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
883 search->current_index = i;
884 goto found;
888 /* then look forwards */
889 for (i=search->current_index+1; i <= dir->count; i++) {
890 if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
891 search->current_index = i;
892 goto found;
897 found:
898 max_count = search->current_index + io->t2fnext.in.max_count;
900 if (max_count > dir->count) {
901 max_count = dir->count;
904 for (i = search->current_index; i < max_count;i++) {
905 ZERO_STRUCT(file);
906 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
907 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
908 unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
909 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
910 file.both_directory_info.name.s = dir->files[i].name;
911 file.both_directory_info.short_name.s = dir->files[i].name;
912 file.both_directory_info.size = dir->files[i].st.st_size;
913 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
915 if (!callback(search_private, &file)) {
916 break;
920 io->t2fnext.out.count = i - search->current_index;
921 io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
923 search->current_index = i;
925 /* work out if we are going to keep the search state */
926 if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
927 ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
928 DLIST_REMOVE(private->search, search);
929 talloc_free(search);
932 return NT_STATUS_OK;
935 /* close a search */
936 static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
937 struct ntvfs_request *req, union smb_search_close *io)
939 struct svfs_private *private = ntvfs->private_data;
940 struct search_state *search;
942 for (search=private->search; search; search = search->next) {
943 if (search->handle == io->findclose.in.handle) break;
946 if (!search) {
947 /* we didn't find the search handle */
948 return NT_STATUS_FOOBAR;
951 DLIST_REMOVE(private->search, search);
952 talloc_free(search);
954 return NT_STATUS_OK;
957 /* SMBtrans - not used on file shares */
958 static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
959 struct ntvfs_request *req, struct smb_trans2 *trans2)
961 return NT_STATUS_ACCESS_DENIED;
966 initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
968 NTSTATUS ntvfs_simple_init(void)
970 NTSTATUS ret;
971 struct ntvfs_ops ops;
972 NTVFS_CURRENT_CRITICAL_SIZES(vers);
974 ZERO_STRUCT(ops);
976 /* fill in all the operations */
977 ops.connect = svfs_connect;
978 ops.disconnect = svfs_disconnect;
979 ops.unlink = svfs_unlink;
980 ops.chkpath = svfs_chkpath;
981 ops.qpathinfo = svfs_qpathinfo;
982 ops.setpathinfo = svfs_setpathinfo;
983 ops.open = svfs_open;
984 ops.mkdir = svfs_mkdir;
985 ops.rmdir = svfs_rmdir;
986 ops.rename = svfs_rename;
987 ops.copy = svfs_copy;
988 ops.ioctl = svfs_ioctl;
989 ops.read = svfs_read;
990 ops.write = svfs_write;
991 ops.seek = svfs_seek;
992 ops.flush = svfs_flush;
993 ops.close = svfs_close;
994 ops.exit = svfs_exit;
995 ops.lock = svfs_lock;
996 ops.setfileinfo = svfs_setfileinfo;
997 ops.qfileinfo = svfs_qfileinfo;
998 ops.fsinfo = svfs_fsinfo;
999 ops.lpq = svfs_lpq;
1000 ops.search_first = svfs_search_first;
1001 ops.search_next = svfs_search_next;
1002 ops.search_close = svfs_search_close;
1003 ops.trans = svfs_trans;
1004 ops.logoff = svfs_logoff;
1005 ops.async_setup = svfs_async_setup;
1006 ops.cancel = svfs_cancel;
1008 /* register ourselves with the NTVFS subsystem. We register
1009 under names 'simple'
1012 ops.type = NTVFS_DISK;
1013 ops.name = "simple";
1014 ret = ntvfs_register(&ops, &vers);
1016 if (!NT_STATUS_IS_OK(ret)) {
1017 DEBUG(0,("Failed to register simple backend with name: %s!\n",
1018 ops.name));
1021 return ret;