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