s4: fix various warnings (not "const" related ones)
[sfrench/samba-autobuild/.git] / source4 / ntvfs / simple / vfs_simple.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    simple NTVFS filesystem backend
5
6    Copyright (C) Andrew Tridgell 2003
7
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.
12    
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.
17    
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/>.
20 */
21 /*
22   this implements a very simple NTVFS filesystem backend. 
23   
24   this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
25   minimal work to give a working backend.
26 */
27
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"
36
37 #ifndef O_DIRECTORY
38 #define O_DIRECTORY 0
39 #endif
40
41 #define CHECK_READ_ONLY(req) do { if (share_bool_option(ntvfs->ctx->config, SHARE_READONLY, true)) return NT_STATUS_ACCESS_DENIED; } while (0)
42
43 /*
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)
48 */
49 static NTSTATUS svfs_connect(struct ntvfs_module_context *ntvfs,
50                              struct ntvfs_request *req,
51                              union smb_tcon* tcon)
52 {
53         struct stat st;
54         struct svfs_private *p;
55         struct share_config *scfg = ntvfs->ctx->config;
56         const char *sharename;
57
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;
70         }
71
72         if (strncmp(sharename, "\\\\", 2) == 0) {
73                 char *p2 = strchr(sharename+2, '\\');
74                 if (p2) {
75                         sharename = p2 + 1;
76                 }
77         }
78
79         p = talloc(ntvfs, struct svfs_private);
80         NT_STATUS_HAVE_NO_MEMORY(p);
81         p->ntvfs = ntvfs;
82         p->next_search_handle = 0;
83         p->connectpath = talloc_strdup(p, share_string_option(scfg, SHARE_PATH, ""));
84         p->open_files = NULL;
85         p->search = NULL;
86
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;
92         }
93
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);
98
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;
102         }
103
104         ntvfs->private_data = p;
105
106         return NT_STATUS_OK;
107 }
108
109 /*
110   disconnect from a share
111 */
112 static NTSTATUS svfs_disconnect(struct ntvfs_module_context *ntvfs)
113 {
114         return NT_STATUS_OK;
115 }
116
117 /*
118   find open file handle given fd
119 */
120 static struct svfs_file *find_fd(struct svfs_private *sp, struct ntvfs_handle *handle)
121 {
122         struct svfs_file *f;
123         void *p;
124
125         p = ntvfs_handle_get_backend_data(handle, sp->ntvfs);
126         if (!p) return NULL;
127
128         f = talloc_get_type(p, struct svfs_file);
129         if (!f) return NULL;
130
131         return f;
132 }
133
134 /*
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)
137 */
138 static NTSTATUS svfs_unlink(struct ntvfs_module_context *ntvfs,
139                             struct ntvfs_request *req,
140                             union smb_unlink *unl)
141 {
142         char *unix_path;
143
144         CHECK_READ_ONLY(req);
145
146         unix_path = svfs_unix_path(ntvfs, req, unl->unlink.in.pattern);
147
148         /* ignoring wildcards ... */
149         if (unlink(unix_path) == -1) {
150                 return map_nt_error_from_unix(errno);
151         }
152
153         return NT_STATUS_OK;
154 }
155
156
157 /*
158   ioctl interface - we don't do any
159 */
160 static NTSTATUS svfs_ioctl(struct ntvfs_module_context *ntvfs,
161                            struct ntvfs_request *req, union smb_ioctl *io)
162 {
163         return NT_STATUS_INVALID_PARAMETER;
164 }
165
166 /*
167   check if a directory exists
168 */
169 static NTSTATUS svfs_chkpath(struct ntvfs_module_context *ntvfs,
170                              struct ntvfs_request *req,
171                              union smb_chkpath *cp)
172 {
173         char *unix_path;
174         struct stat st;
175
176         unix_path = svfs_unix_path(ntvfs, req, cp->chkpath.in.path);
177
178         if (stat(unix_path, &st) == -1) {
179                 return map_nt_error_from_unix(errno);
180         }
181
182         if (!S_ISDIR(st.st_mode)) {
183                 return NT_STATUS_NOT_A_DIRECTORY;
184         }
185
186         return NT_STATUS_OK;
187 }
188
189 /*
190   build a file_id from a stat struct
191 */
192 static uint64_t svfs_file_id(struct stat *st)
193 {
194         uint64_t ret = st->st_ino;
195         ret <<= 32;
196         ret |= st->st_dev;
197         return ret;
198 }
199
200 /*
201   approximately map a struct stat to a generic fileinfo struct
202 */
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)
206 {
207         struct svfs_dir *dir = NULL;
208         char *pattern = NULL;
209         int i;
210         const char *s, *short_name;
211
212         s = strrchr(unix_path, '/');
213         if (s) {
214                 short_name = s+1;
215         } else {
216                 short_name = "";
217         }
218
219         asprintf(&pattern, "%s:*", unix_path);
220         
221         if (pattern) {
222                 dir = svfs_list_unix(req, req, pattern);
223         }
224
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;
247         
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;
261         }
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");
265
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;
271         }
272
273         return NT_STATUS_OK;
274 }
275
276 /*
277   return info on a pathname
278 */
279 static NTSTATUS svfs_qpathinfo(struct ntvfs_module_context *ntvfs,
280                                struct ntvfs_request *req, union smb_fileinfo *info)
281 {
282         char *unix_path;
283         struct stat st;
284
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);
288         }
289         
290         unix_path = svfs_unix_path(ntvfs, req, info->generic.in.file.path);
291         DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path));
292         if (stat(unix_path, &st) == -1) {
293                 DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno));
294                 return map_nt_error_from_unix(errno);
295         }
296         DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path));
297         return svfs_map_fileinfo(ntvfs, req, info, &st, unix_path);
298 }
299
300 /*
301   query info on a open file
302 */
303 static NTSTATUS svfs_qfileinfo(struct ntvfs_module_context *ntvfs,
304                                struct ntvfs_request *req, union smb_fileinfo *info)
305 {
306         struct svfs_private *p = ntvfs->private_data;
307         struct svfs_file *f;
308         struct stat st;
309
310         if (info->generic.level != RAW_FILEINFO_GENERIC) {
311                 return ntvfs_map_qfileinfo(ntvfs, req, info);
312         }
313
314         f = find_fd(p, info->generic.in.file.ntvfs);
315         if (!f) {
316                 return NT_STATUS_INVALID_HANDLE;
317         }
318         
319         if (fstat(f->fd, &st) == -1) {
320                 return map_nt_error_from_unix(errno);
321         }
322
323         return svfs_map_fileinfo(ntvfs, req,info, &st, f->name);
324 }
325
326
327 /*
328   open a file
329 */
330 static NTSTATUS svfs_open(struct ntvfs_module_context *ntvfs,
331                           struct ntvfs_request *req, union smb_open *io)
332 {
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;
342         
343         if (io->generic.level != RAW_OPEN_GENERIC) {
344                 return ntvfs_map_open(ntvfs, req, io);
345         }
346
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;
354         }
355
356         unix_path = svfs_unix_path(ntvfs, req, io->ntcreatex.in.fname);
357
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;
376         }
377         
378         flags |= rdwr_flags;
379
380         if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
381                 flags = O_RDONLY | O_DIRECTORY;
382                 if (readonly) {
383                         goto do_open;
384                 }
385                 switch (io->generic.in.open_disposition) {
386                 case NTCREATEX_DISP_CREATE:
387                         if (mkdir(unix_path, 0755) == -1) {
388                                 DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
389                                 return map_nt_error_from_unix(errno);
390                         }
391                         break;
392                 case NTCREATEX_DISP_OPEN_IF:
393                         if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
394                                 DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
395                                 return map_nt_error_from_unix(errno);
396                         }
397                         break;
398                 }
399         }
400
401 do_open:
402         fd = open(unix_path, flags, 0644);
403         if (fd == -1) {
404                 return map_nt_error_from_unix(errno);
405         }
406
407         if (fstat(fd, &st) == -1) {
408                 DEBUG(9,("svfs_open: fstat errno=%d\n", errno));
409                 close(fd);
410                 return map_nt_error_from_unix(errno);
411         }
412
413         status = ntvfs_handle_new(ntvfs, req, &handle);
414         NT_STATUS_NOT_OK_RETURN(status);
415
416         f = talloc(handle, struct svfs_file);
417         NT_STATUS_HAVE_NO_MEMORY(f);
418         f->fd = fd;
419         f->name = talloc_strdup(f, unix_path);
420         NT_STATUS_HAVE_NO_MEMORY(f->name);
421
422         DLIST_ADD(p->open_files, f);
423
424         status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
425         NT_STATUS_NOT_OK_RETURN(status);
426
427         ZERO_STRUCT(io->generic.out);
428         
429         unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
430         unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
431         unix_to_nt_time(&io->generic.out.write_time,  st.st_mtime);
432         unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
433         io->generic.out.file.ntvfs = handle;
434         io->generic.out.alloc_size = st.st_size;
435         io->generic.out.size = st.st_size;
436         io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
437         io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
438
439         return NT_STATUS_OK;
440 }
441
442 /*
443   create a directory
444 */
445 static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
446                            struct ntvfs_request *req, union smb_mkdir *md)
447 {
448         char *unix_path;
449
450         CHECK_READ_ONLY(req);
451
452         if (md->generic.level != RAW_MKDIR_MKDIR) {
453                 return NT_STATUS_INVALID_LEVEL;
454         }
455
456         unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
457
458         if (mkdir(unix_path, 0777) == -1) {
459                 return map_nt_error_from_unix(errno);
460         }
461
462         return NT_STATUS_OK;
463 }
464
465 /*
466   remove a directory
467 */
468 static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
469                            struct ntvfs_request *req, struct smb_rmdir *rd)
470 {
471         char *unix_path;
472
473         CHECK_READ_ONLY(req);
474
475         unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
476
477         if (rmdir(unix_path) == -1) {
478                 return map_nt_error_from_unix(errno);
479         }
480
481         return NT_STATUS_OK;
482 }
483
484 /*
485   rename a set of files
486 */
487 static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
488                             struct ntvfs_request *req, union smb_rename *ren)
489 {
490         char *unix_path1, *unix_path2;
491
492         CHECK_READ_ONLY(req);
493
494         if (ren->generic.level != RAW_RENAME_RENAME) {
495                 return NT_STATUS_INVALID_LEVEL;
496         }
497
498         unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
499         unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
500
501         if (rename(unix_path1, unix_path2) == -1) {
502                 return map_nt_error_from_unix(errno);
503         }
504         
505         return NT_STATUS_OK;
506 }
507
508 /*
509   copy a set of files
510 */
511 static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
512                           struct ntvfs_request *req, struct smb_copy *cp)
513 {
514         return NT_STATUS_NOT_SUPPORTED;
515 }
516
517 /*
518   read from a file
519 */
520 static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
521                           struct ntvfs_request *req, union smb_read *rd)
522 {
523         struct svfs_private *p = ntvfs->private_data;
524         struct svfs_file *f;
525         ssize_t ret;
526
527         if (rd->generic.level != RAW_READ_READX) {
528                 return NT_STATUS_NOT_SUPPORTED;
529         }
530
531         f = find_fd(p, rd->readx.in.file.ntvfs);
532         if (!f) {
533                 return NT_STATUS_INVALID_HANDLE;
534         }
535
536         ret = pread(f->fd, 
537                     rd->readx.out.data, 
538                     rd->readx.in.maxcnt,
539                     rd->readx.in.offset);
540         if (ret == -1) {
541                 return map_nt_error_from_unix(errno);
542         }
543
544         rd->readx.out.nread = ret;
545         rd->readx.out.remaining = 0; /* should fill this in? */
546         rd->readx.out.compaction_mode = 0; 
547
548         return NT_STATUS_OK;
549 }
550
551 /*
552   write to a file
553 */
554 static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
555                            struct ntvfs_request *req, union smb_write *wr)
556 {
557         struct svfs_private *p = ntvfs->private_data;
558         struct svfs_file *f;
559         ssize_t ret;
560
561         if (wr->generic.level != RAW_WRITE_WRITEX) {
562                 return ntvfs_map_write(ntvfs, req, wr);
563         }
564
565         CHECK_READ_ONLY(req);
566
567         f = find_fd(p, wr->writex.in.file.ntvfs);
568         if (!f) {
569                 return NT_STATUS_INVALID_HANDLE;
570         }
571
572         ret = pwrite(f->fd, 
573                      wr->writex.in.data, 
574                      wr->writex.in.count,
575                      wr->writex.in.offset);
576         if (ret == -1) {
577                 return map_nt_error_from_unix(errno);
578         }
579                 
580         wr->writex.out.nwritten = ret;
581         wr->writex.out.remaining = 0; /* should fill this in? */
582         
583         return NT_STATUS_OK;
584 }
585
586 /*
587   seek in a file
588 */
589 static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
590                           struct ntvfs_request *req,
591                           union smb_seek *io)
592 {
593         return NT_STATUS_NOT_SUPPORTED;
594 }
595
596 /*
597   flush a file
598 */
599 static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
600                            struct ntvfs_request *req,
601                            union smb_flush *io)
602 {
603         struct svfs_private *p = ntvfs->private_data;
604         struct svfs_file *f;
605
606         switch (io->generic.level) {
607         case RAW_FLUSH_FLUSH:
608         case RAW_FLUSH_SMB2:
609                 /* ignore the additional unknown option in SMB2 */
610                 f = find_fd(p, io->generic.in.file.ntvfs);
611                 if (!f) {
612                         return NT_STATUS_INVALID_HANDLE;
613                 }
614                 fsync(f->fd);
615                 return NT_STATUS_OK;
616
617         case RAW_FLUSH_ALL:
618                 for (f=p->open_files;f;f=f->next) {
619                         fsync(f->fd);
620                 }
621                 return NT_STATUS_OK;
622         }
623
624         return NT_STATUS_INVALID_LEVEL;
625 }
626
627 /*
628   close a file
629 */
630 static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
631                            struct ntvfs_request *req,
632                            union smb_close *io)
633 {
634         struct svfs_private *p = ntvfs->private_data;
635         struct svfs_file *f;
636
637         if (io->generic.level != RAW_CLOSE_CLOSE) {
638                 /* we need a mapping function */
639                 return NT_STATUS_INVALID_LEVEL;
640         }
641
642         f = find_fd(p, io->close.in.file.ntvfs);
643         if (!f) {
644                 return NT_STATUS_INVALID_HANDLE;
645         }
646
647         if (close(f->fd) == -1) {
648                 return map_nt_error_from_unix(errno);
649         }
650
651         DLIST_REMOVE(p->open_files, f);
652         talloc_free(f->name);
653         talloc_free(f);
654
655         return NT_STATUS_OK;
656 }
657
658 /*
659   exit - closing files
660 */
661 static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
662                           struct ntvfs_request *req)
663 {
664         return NT_STATUS_NOT_SUPPORTED;
665 }
666
667 /*
668   logoff - closing files
669 */
670 static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
671                             struct ntvfs_request *req)
672 {
673         return NT_STATUS_NOT_SUPPORTED;
674 }
675
676 /*
677   setup for an async call
678 */
679 static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
680                                  struct ntvfs_request *req, 
681                                  void *private_data)
682 {
683         return NT_STATUS_OK;
684 }
685
686 /*
687   cancel an async call
688 */
689 static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
690 {
691         return NT_STATUS_UNSUCCESSFUL;
692 }
693
694 /*
695   lock a byte range
696 */
697 static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
698                           struct ntvfs_request *req, union smb_lock *lck)
699 {
700         DEBUG(0,("REWRITE: not doing byte range locking!\n"));
701         return NT_STATUS_OK;
702 }
703
704 /*
705   set info on a pathname
706 */
707 static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
708                                  struct ntvfs_request *req, union smb_setfileinfo *st)
709 {
710         CHECK_READ_ONLY(req);
711
712         return NT_STATUS_NOT_SUPPORTED;
713 }
714
715 /*
716   set info on a open file
717 */
718 static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
719                                  struct ntvfs_request *req, 
720                                  union smb_setfileinfo *info)
721 {
722         struct svfs_private *p = ntvfs->private_data;
723         struct svfs_file *f;
724         struct utimbuf unix_times;
725
726         CHECK_READ_ONLY(req);
727
728         f = find_fd(p, info->generic.in.file.ntvfs);
729         if (!f) {
730                 return NT_STATUS_INVALID_HANDLE;
731         }
732         
733         switch (info->generic.level) {
734         case RAW_SFILEINFO_END_OF_FILE_INFO:
735         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
736                 if (ftruncate(f->fd, 
737                               info->end_of_file_info.in.size) == -1) {
738                         return map_nt_error_from_unix(errno);
739                 }
740                 break;
741         case RAW_SFILEINFO_SETATTRE:
742                 unix_times.actime = info->setattre.in.access_time;
743                 unix_times.modtime = info->setattre.in.write_time;
744
745                 if (unix_times.actime == 0 && unix_times.modtime == 0) {
746                         break;
747                 } 
748
749                 /* set modify time = to access time if modify time was 0 */
750                 if (unix_times.actime != 0 && unix_times.modtime == 0) {
751                         unix_times.modtime = unix_times.actime;
752                 }
753
754                 /* Set the date on this file */
755                 if (svfs_file_utime(f->fd, &unix_times) != 0) {
756                         return NT_STATUS_ACCESS_DENIED;
757                 }
758                 break;
759         default:
760                 DEBUG(2,("svfs_setfileinfo: level %d not implemented\n", 
761                          info->generic.level));
762                 return NT_STATUS_NOT_IMPLEMENTED;
763         }
764         return NT_STATUS_OK;
765 }
766
767
768 /*
769   return filesystem space info
770 */
771 static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
772                             struct ntvfs_request *req, union smb_fsinfo *fs)
773 {
774         struct svfs_private *p = ntvfs->private_data;
775         struct stat st;
776
777         if (fs->generic.level != RAW_QFS_GENERIC) {
778                 return ntvfs_map_fsinfo(ntvfs, req, fs);
779         }
780
781         if (sys_fsusage(p->connectpath,
782                         &fs->generic.out.blocks_free, 
783                         &fs->generic.out.blocks_total) == -1) {
784                 return map_nt_error_from_unix(errno);
785         }
786
787         fs->generic.out.block_size = 512;
788
789         if (stat(p->connectpath, &st) != 0) {
790                 return NT_STATUS_DISK_CORRUPT_ERROR;
791         }
792         
793         fs->generic.out.fs_id = st.st_ino;
794         unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
795         fs->generic.out.serial_number = st.st_ino;
796         fs->generic.out.fs_attr = 0;
797         fs->generic.out.max_file_component_length = 255;
798         fs->generic.out.device_type = 0;
799         fs->generic.out.device_characteristics = 0;
800         fs->generic.out.quota_soft = 0;
801         fs->generic.out.quota_hard = 0;
802         fs->generic.out.quota_flags = 0;
803         fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
804         fs->generic.out.fs_type = ntvfs->ctx->fs_type;
805
806         return NT_STATUS_OK;
807 }
808
809 #if 0
810 /*
811   return filesystem attribute info
812 */
813 static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
814                             struct ntvfs_request *req, union smb_fsattr *fs)
815 {
816         struct stat st;
817         struct svfs_private *p = ntvfs->private_data;
818
819         if (fs->generic.level != RAW_FSATTR_GENERIC) {
820                 return ntvfs_map_fsattr(ntvfs, req, fs);
821         }
822
823         if (stat(p->connectpath, &st) == -1) {
824                 return map_nt_error_from_unix(errno);
825         }
826
827         unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
828         fs->generic.out.fs_attr = 
829                 FILE_CASE_PRESERVED_NAMES | 
830                 FILE_CASE_SENSITIVE_SEARCH | 
831                 FILE_PERSISTENT_ACLS;
832         fs->generic.out.max_file_component_length = 255;
833         fs->generic.out.serial_number = 1;
834         fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
835         fs->generic.out.volume_name = talloc_strdup(req, 
836                                                     lp_servicename(req->tcon->service));
837
838         return NT_STATUS_OK;
839 }
840 #endif
841
842 /*
843   return print queue info
844 */
845 static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
846                          struct ntvfs_request *req, union smb_lpq *lpq)
847 {
848         return NT_STATUS_NOT_SUPPORTED;
849 }
850
851 /* 
852    list files in a directory matching a wildcard pattern
853 */
854 static NTSTATUS svfs_search_first(struct ntvfs_module_context *ntvfs,
855                                   struct ntvfs_request *req, union smb_search_first *io, 
856                                   void *search_private, 
857                                   bool (*callback)(void *, const union smb_search_data *))
858 {
859         struct svfs_dir *dir;
860         int i;
861         struct svfs_private *p = ntvfs->private_data;
862         struct search_state *search;
863         union smb_search_data file;
864         uint_t max_count;
865
866         if (io->generic.level != RAW_SEARCH_TRANS2) {
867                 return NT_STATUS_NOT_SUPPORTED;
868         }
869
870         if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
871                 return NT_STATUS_NOT_SUPPORTED;
872         }
873
874         search = talloc_zero(p, struct search_state);
875         if (!search) {
876                 return NT_STATUS_NO_MEMORY;
877         }
878
879         max_count = io->t2ffirst.in.max_count;
880
881         dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
882         if (!dir) {
883                 return NT_STATUS_FOOBAR;
884         }
885
886         search->handle = p->next_search_handle;
887         search->dir = dir;
888
889         if (dir->count < max_count) {
890                 max_count = dir->count;
891         }
892
893         for (i=0; i < max_count;i++) {
894                 ZERO_STRUCT(file);
895                 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
896                 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
897                 unix_to_nt_time(&file.both_directory_info.write_time,  dir->files[i].st.st_mtime);
898                 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
899                 file.both_directory_info.name.s = dir->files[i].name;
900                 file.both_directory_info.short_name.s = dir->files[i].name;
901                 file.both_directory_info.size = dir->files[i].st.st_size;
902                 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
903
904                 if (!callback(search_private, &file)) {
905                         break;
906                 }
907         }
908
909         search->current_index = i;
910
911         io->t2ffirst.out.count = i;
912         io->t2ffirst.out.handle = search->handle;
913         io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
914
915         /* work out if we are going to keep the search state */
916         if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
917             ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
918                 talloc_free(search);
919         } else {
920                 p->next_search_handle++;
921                 DLIST_ADD(p->search, search);
922         }
923
924         return NT_STATUS_OK;
925 }
926
927 /* continue a search */
928 static NTSTATUS svfs_search_next(struct ntvfs_module_context *ntvfs,
929                                  struct ntvfs_request *req, union smb_search_next *io, 
930                                  void *search_private, 
931                                  bool (*callback)(void *, const union smb_search_data *))
932 {
933         struct svfs_dir *dir;
934         int i;
935         struct svfs_private *p = ntvfs->private_data;
936         struct search_state *search;
937         union smb_search_data file;
938         uint_t max_count;
939
940         if (io->generic.level != RAW_SEARCH_TRANS2) {
941                 return NT_STATUS_NOT_SUPPORTED;
942         }
943
944         if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
945                 return NT_STATUS_NOT_SUPPORTED;
946         }
947
948         for (search=p->search; search; search = search->next) {
949                 if (search->handle == io->t2fnext.in.handle) break;
950         }
951         
952         if (!search) {
953                 /* we didn't find the search handle */
954                 return NT_STATUS_FOOBAR;
955         }
956
957         dir = search->dir;
958
959         /* the client might be asking for something other than just continuing
960            with the search */
961         if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
962             (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
963             io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
964                 /* look backwards first */
965                 for (i=search->current_index; i > 0; i--) {
966                         if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
967                                 search->current_index = i;
968                                 goto found;
969                         }
970                 }
971
972                 /* then look forwards */
973                 for (i=search->current_index+1; i <= dir->count; i++) {
974                         if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
975                                 search->current_index = i;
976                                 goto found;
977                         }
978                 }
979         }
980
981 found:  
982         max_count = search->current_index + io->t2fnext.in.max_count;
983
984         if (max_count > dir->count) {
985                 max_count = dir->count;
986         }
987
988         for (i = search->current_index; i < max_count;i++) {
989                 ZERO_STRUCT(file);
990                 unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
991                 unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
992                 unix_to_nt_time(&file.both_directory_info.write_time,  dir->files[i].st.st_mtime);
993                 unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
994                 file.both_directory_info.name.s = dir->files[i].name;
995                 file.both_directory_info.short_name.s = dir->files[i].name;
996                 file.both_directory_info.size = dir->files[i].st.st_size;
997                 file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
998
999                 if (!callback(search_private, &file)) {
1000                         break;
1001                 }
1002         }
1003
1004         io->t2fnext.out.count = i - search->current_index;
1005         io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
1006
1007         search->current_index = i;
1008
1009         /* work out if we are going to keep the search state */
1010         if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
1011             ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
1012                 DLIST_REMOVE(p->search, search);
1013                 talloc_free(search);
1014         }
1015
1016         return NT_STATUS_OK;
1017 }
1018
1019 /* close a search */
1020 static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
1021                                   struct ntvfs_request *req, union smb_search_close *io)
1022 {
1023         struct svfs_private *p = ntvfs->private_data;
1024         struct search_state *search;
1025
1026         for (search=p->search; search; search = search->next) {
1027                 if (search->handle == io->findclose.in.handle) break;
1028         }
1029         
1030         if (!search) {
1031                 /* we didn't find the search handle */
1032                 return NT_STATUS_FOOBAR;
1033         }
1034
1035         DLIST_REMOVE(p->search, search);
1036         talloc_free(search);
1037
1038         return NT_STATUS_OK;
1039 }
1040
1041 /* SMBtrans - not used on file shares */
1042 static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
1043                            struct ntvfs_request *req, struct smb_trans2 *trans2)
1044 {
1045         return NT_STATUS_ACCESS_DENIED;
1046 }
1047
1048
1049 /*
1050   initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
1051  */
1052 NTSTATUS ntvfs_simple_init(void)
1053 {
1054         NTSTATUS ret;
1055         struct ntvfs_ops ops;
1056         NTVFS_CURRENT_CRITICAL_SIZES(vers);
1057
1058         ZERO_STRUCT(ops);
1059
1060         /* fill in all the operations */
1061         ops.connect = svfs_connect;
1062         ops.disconnect = svfs_disconnect;
1063         ops.unlink = svfs_unlink;
1064         ops.chkpath = svfs_chkpath;
1065         ops.qpathinfo = svfs_qpathinfo;
1066         ops.setpathinfo = svfs_setpathinfo;
1067         ops.open = svfs_open;
1068         ops.mkdir = svfs_mkdir;
1069         ops.rmdir = svfs_rmdir;
1070         ops.rename = svfs_rename;
1071         ops.copy = svfs_copy;
1072         ops.ioctl = svfs_ioctl;
1073         ops.read = svfs_read;
1074         ops.write = svfs_write;
1075         ops.seek = svfs_seek;
1076         ops.flush = svfs_flush; 
1077         ops.close = svfs_close;
1078         ops.exit = svfs_exit;
1079         ops.lock = svfs_lock;
1080         ops.setfileinfo = svfs_setfileinfo;
1081         ops.qfileinfo = svfs_qfileinfo;
1082         ops.fsinfo = svfs_fsinfo;
1083         ops.lpq = svfs_lpq;
1084         ops.search_first = svfs_search_first;
1085         ops.search_next = svfs_search_next;
1086         ops.search_close = svfs_search_close;
1087         ops.trans = svfs_trans;
1088         ops.logoff = svfs_logoff;
1089         ops.async_setup = svfs_async_setup;
1090         ops.cancel = svfs_cancel;
1091
1092         /* register ourselves with the NTVFS subsystem. We register
1093            under names 'simple'
1094         */
1095
1096         ops.type = NTVFS_DISK;
1097         ops.name = "simple";
1098         ret = ntvfs_register(&ops, &vers);
1099
1100         if (!NT_STATUS_IS_OK(ret)) {
1101                 DEBUG(0,("Failed to register simple backend with name: %s!\n",
1102                          ops.name));
1103         }
1104
1105         return ret;
1106 }