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