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
= talloc_strdup(p
, share_string_option(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(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(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 asprintf(&pattern
, "%s:*", unix_path
);
222 dir
= svfs_list_unix(req
, req
, pattern
);
225 unix_to_nt_time(&info
->generic
.out
.create_time
, st
->st_ctime
);
226 unix_to_nt_time(&info
->generic
.out
.access_time
, st
->st_atime
);
227 unix_to_nt_time(&info
->generic
.out
.write_time
, st
->st_mtime
);
228 unix_to_nt_time(&info
->generic
.out
.change_time
, st
->st_mtime
);
229 info
->generic
.out
.alloc_size
= st
->st_size
;
230 info
->generic
.out
.size
= st
->st_size
;
231 info
->generic
.out
.attrib
= svfs_unix_to_dos_attrib(st
->st_mode
);
232 info
->generic
.out
.alloc_size
= st
->st_blksize
* st
->st_blocks
;
233 info
->generic
.out
.nlink
= st
->st_nlink
;
234 info
->generic
.out
.directory
= S_ISDIR(st
->st_mode
) ? 1 : 0;
235 info
->generic
.out
.file_id
= svfs_file_id(st
);
236 /* REWRITE: TODO stuff in here */
237 info
->generic
.out
.delete_pending
= 0;
238 info
->generic
.out
.ea_size
= 0;
239 info
->generic
.out
.num_eas
= 0;
240 info
->generic
.out
.fname
.s
= talloc_strdup(req
, short_name
);
241 info
->generic
.out
.alt_fname
.s
= talloc_strdup(req
, short_name
);
242 info
->generic
.out
.compressed_size
= 0;
243 info
->generic
.out
.format
= 0;
244 info
->generic
.out
.unit_shift
= 0;
245 info
->generic
.out
.chunk_shift
= 0;
246 info
->generic
.out
.cluster_shift
= 0;
248 info
->generic
.out
.access_flags
= 0;
249 info
->generic
.out
.position
= 0;
250 info
->generic
.out
.mode
= 0;
251 info
->generic
.out
.alignment_requirement
= 0;
252 info
->generic
.out
.reparse_tag
= 0;
253 info
->generic
.out
.num_streams
= 0;
254 /* setup a single data stream */
255 info
->generic
.out
.num_streams
= 1 + (dir
?dir
->count
:0);
256 info
->generic
.out
.streams
= talloc_array(req
,
257 struct stream_struct
,
258 info
->generic
.out
.num_streams
);
259 if (!info
->generic
.out
.streams
) {
260 return NT_STATUS_NO_MEMORY
;
262 info
->generic
.out
.streams
[0].size
= st
->st_size
;
263 info
->generic
.out
.streams
[0].alloc_size
= st
->st_size
;
264 info
->generic
.out
.streams
[0].stream_name
.s
= talloc_strdup(req
,"::$DATA");
266 for (i
=0;dir
&& i
<dir
->count
;i
++) {
267 s
= strchr(dir
->files
[i
].name
, ':');
268 info
->generic
.out
.streams
[1+i
].size
= dir
->files
[i
].st
.st_size
;
269 info
->generic
.out
.streams
[1+i
].alloc_size
= dir
->files
[i
].st
.st_size
;
270 info
->generic
.out
.streams
[1+i
].stream_name
.s
= s
?s
:dir
->files
[i
].name
;
277 return info on a pathname
279 static NTSTATUS
svfs_qpathinfo(struct ntvfs_module_context
*ntvfs
,
280 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
285 DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info
->generic
.in
.file
.path
, info
->generic
.level
));
286 if (info
->generic
.level
!= RAW_FILEINFO_GENERIC
) {
287 return ntvfs_map_qpathinfo(ntvfs
, req
, info
);
290 unix_path
= svfs_unix_path(ntvfs
, req
, info
->generic
.in
.file
.path
);
291 DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path
));
292 if (stat(unix_path
, &st
) == -1) {
293 DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path
, errno
));
294 return map_nt_error_from_unix(errno
);
296 DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path
));
297 return svfs_map_fileinfo(ntvfs
, req
, info
, &st
, unix_path
);
301 query info on a open file
303 static NTSTATUS
svfs_qfileinfo(struct ntvfs_module_context
*ntvfs
,
304 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
306 struct svfs_private
*p
= ntvfs
->private_data
;
310 if (info
->generic
.level
!= RAW_FILEINFO_GENERIC
) {
311 return ntvfs_map_qfileinfo(ntvfs
, req
, info
);
314 f
= find_fd(p
, info
->generic
.in
.file
.ntvfs
);
316 return NT_STATUS_INVALID_HANDLE
;
319 if (fstat(f
->fd
, &st
) == -1) {
320 return map_nt_error_from_unix(errno
);
323 return svfs_map_fileinfo(ntvfs
, req
,info
, &st
, f
->name
);
330 static NTSTATUS
svfs_open(struct ntvfs_module_context
*ntvfs
,
331 struct ntvfs_request
*req
, union smb_open
*io
)
333 struct svfs_private
*p
= ntvfs
->private_data
;
338 int create_flags
, rdwr_flags
;
341 struct ntvfs_handle
*handle
;
343 if (io
->generic
.level
!= RAW_OPEN_GENERIC
) {
344 return ntvfs_map_open(ntvfs
, req
, io
);
347 readonly
= share_bool_option(ntvfs
->ctx
->config
, SHARE_READONLY
, SHARE_READONLY_DEFAULT
);
350 rdwr_flags
= O_RDONLY
;
352 create_flags
= O_CREAT
;
356 unix_path
= svfs_unix_path(ntvfs
, req
, io
->ntcreatex
.in
.fname
);
358 switch (io
->generic
.in
.open_disposition
) {
359 case NTCREATEX_DISP_SUPERSEDE
:
360 case NTCREATEX_DISP_OVERWRITE_IF
:
361 flags
= create_flags
| O_TRUNC
;
363 case NTCREATEX_DISP_OPEN
:
364 case NTCREATEX_DISP_OVERWRITE
:
367 case NTCREATEX_DISP_CREATE
:
368 flags
= create_flags
| O_EXCL
;
370 case NTCREATEX_DISP_OPEN_IF
:
371 flags
= create_flags
;
380 if (io
->generic
.in
.create_options
& NTCREATEX_OPTIONS_DIRECTORY
) {
381 flags
= O_RDONLY
| O_DIRECTORY
;
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
);
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
);
402 fd
= open(unix_path
, flags
, 0644);
404 return map_nt_error_from_unix(errno
);
407 if (fstat(fd
, &st
) == -1) {
408 DEBUG(9,("svfs_open: fstat errno=%d\n", errno
));
410 return map_nt_error_from_unix(errno
);
413 status
= ntvfs_handle_new(ntvfs
, req
, &handle
);
414 NT_STATUS_NOT_OK_RETURN(status
);
416 f
= talloc(handle
, struct svfs_file
);
417 NT_STATUS_HAVE_NO_MEMORY(f
);
419 f
->name
= talloc_strdup(f
, unix_path
);
420 NT_STATUS_HAVE_NO_MEMORY(f
->name
);
422 DLIST_ADD(p
->open_files
, f
);
424 status
= ntvfs_handle_set_backend_data(handle
, ntvfs
, f
);
425 NT_STATUS_NOT_OK_RETURN(status
);
427 ZERO_STRUCT(io
->generic
.out
);
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;
445 static NTSTATUS
svfs_mkdir(struct ntvfs_module_context
*ntvfs
,
446 struct ntvfs_request
*req
, union smb_mkdir
*md
)
450 CHECK_READ_ONLY(req
);
452 if (md
->generic
.level
!= RAW_MKDIR_MKDIR
) {
453 return NT_STATUS_INVALID_LEVEL
;
456 unix_path
= svfs_unix_path(ntvfs
, req
, md
->mkdir
.in
.path
);
458 if (mkdir(unix_path
, 0777) == -1) {
459 return map_nt_error_from_unix(errno
);
468 static NTSTATUS
svfs_rmdir(struct ntvfs_module_context
*ntvfs
,
469 struct ntvfs_request
*req
, struct smb_rmdir
*rd
)
473 CHECK_READ_ONLY(req
);
475 unix_path
= svfs_unix_path(ntvfs
, req
, rd
->in
.path
);
477 if (rmdir(unix_path
) == -1) {
478 return map_nt_error_from_unix(errno
);
485 rename a set of files
487 static NTSTATUS
svfs_rename(struct ntvfs_module_context
*ntvfs
,
488 struct ntvfs_request
*req
, union smb_rename
*ren
)
490 char *unix_path1
, *unix_path2
;
492 CHECK_READ_ONLY(req
);
494 if (ren
->generic
.level
!= RAW_RENAME_RENAME
) {
495 return NT_STATUS_INVALID_LEVEL
;
498 unix_path1
= svfs_unix_path(ntvfs
, req
, ren
->rename
.in
.pattern1
);
499 unix_path2
= svfs_unix_path(ntvfs
, req
, ren
->rename
.in
.pattern2
);
501 if (rename(unix_path1
, unix_path2
) == -1) {
502 return map_nt_error_from_unix(errno
);
511 static NTSTATUS
svfs_copy(struct ntvfs_module_context
*ntvfs
,
512 struct ntvfs_request
*req
, struct smb_copy
*cp
)
514 return NT_STATUS_NOT_SUPPORTED
;
520 static NTSTATUS
svfs_read(struct ntvfs_module_context
*ntvfs
,
521 struct ntvfs_request
*req
, union smb_read
*rd
)
523 struct svfs_private
*p
= ntvfs
->private_data
;
527 if (rd
->generic
.level
!= RAW_READ_READX
) {
528 return NT_STATUS_NOT_SUPPORTED
;
531 f
= find_fd(p
, rd
->readx
.in
.file
.ntvfs
);
533 return NT_STATUS_INVALID_HANDLE
;
539 rd
->readx
.in
.offset
);
541 return map_nt_error_from_unix(errno
);
544 rd
->readx
.out
.nread
= ret
;
545 rd
->readx
.out
.remaining
= 0; /* should fill this in? */
546 rd
->readx
.out
.compaction_mode
= 0;
554 static NTSTATUS
svfs_write(struct ntvfs_module_context
*ntvfs
,
555 struct ntvfs_request
*req
, union smb_write
*wr
)
557 struct svfs_private
*p
= ntvfs
->private_data
;
561 if (wr
->generic
.level
!= RAW_WRITE_WRITEX
) {
562 return ntvfs_map_write(ntvfs
, req
, wr
);
565 CHECK_READ_ONLY(req
);
567 f
= find_fd(p
, wr
->writex
.in
.file
.ntvfs
);
569 return NT_STATUS_INVALID_HANDLE
;
575 wr
->writex
.in
.offset
);
577 return map_nt_error_from_unix(errno
);
580 wr
->writex
.out
.nwritten
= ret
;
581 wr
->writex
.out
.remaining
= 0; /* should fill this in? */
589 static NTSTATUS
svfs_seek(struct ntvfs_module_context
*ntvfs
,
590 struct ntvfs_request
*req
,
593 return NT_STATUS_NOT_SUPPORTED
;
599 static NTSTATUS
svfs_flush(struct ntvfs_module_context
*ntvfs
,
600 struct ntvfs_request
*req
,
603 struct svfs_private
*p
= ntvfs
->private_data
;
606 switch (io
->generic
.level
) {
607 case RAW_FLUSH_FLUSH
:
609 /* ignore the additional unknown option in SMB2 */
610 f
= find_fd(p
, io
->generic
.in
.file
.ntvfs
);
612 return NT_STATUS_INVALID_HANDLE
;
618 for (f
=p
->open_files
;f
;f
=f
->next
) {
624 return NT_STATUS_INVALID_LEVEL
;
630 static NTSTATUS
svfs_close(struct ntvfs_module_context
*ntvfs
,
631 struct ntvfs_request
*req
,
634 struct svfs_private
*p
= ntvfs
->private_data
;
637 if (io
->generic
.level
!= RAW_CLOSE_CLOSE
) {
638 /* we need a mapping function */
639 return NT_STATUS_INVALID_LEVEL
;
642 f
= find_fd(p
, io
->close
.in
.file
.ntvfs
);
644 return NT_STATUS_INVALID_HANDLE
;
647 if (close(f
->fd
) == -1) {
648 return map_nt_error_from_unix(errno
);
651 DLIST_REMOVE(p
->open_files
, f
);
652 talloc_free(f
->name
);
661 static NTSTATUS
svfs_exit(struct ntvfs_module_context
*ntvfs
,
662 struct ntvfs_request
*req
)
664 return NT_STATUS_NOT_SUPPORTED
;
668 logoff - closing files
670 static NTSTATUS
svfs_logoff(struct ntvfs_module_context
*ntvfs
,
671 struct ntvfs_request
*req
)
673 return NT_STATUS_NOT_SUPPORTED
;
677 setup for an async call
679 static NTSTATUS
svfs_async_setup(struct ntvfs_module_context
*ntvfs
,
680 struct ntvfs_request
*req
,
689 static NTSTATUS
svfs_cancel(struct ntvfs_module_context
*ntvfs
, struct ntvfs_request
*req
)
691 return NT_STATUS_UNSUCCESSFUL
;
697 static NTSTATUS
svfs_lock(struct ntvfs_module_context
*ntvfs
,
698 struct ntvfs_request
*req
, union smb_lock
*lck
)
700 DEBUG(0,("REWRITE: not doing byte range locking!\n"));
705 set info on a pathname
707 static NTSTATUS
svfs_setpathinfo(struct ntvfs_module_context
*ntvfs
,
708 struct ntvfs_request
*req
, union smb_setfileinfo
*st
)
710 CHECK_READ_ONLY(req
);
712 return NT_STATUS_NOT_SUPPORTED
;
716 set info on a open file
718 static NTSTATUS
svfs_setfileinfo(struct ntvfs_module_context
*ntvfs
,
719 struct ntvfs_request
*req
,
720 union smb_setfileinfo
*info
)
722 struct svfs_private
*p
= ntvfs
->private_data
;
724 struct utimbuf unix_times
;
726 CHECK_READ_ONLY(req
);
728 f
= find_fd(p
, info
->generic
.in
.file
.ntvfs
);
730 return NT_STATUS_INVALID_HANDLE
;
733 switch (info
->generic
.level
) {
734 case RAW_SFILEINFO_END_OF_FILE_INFO
:
735 case RAW_SFILEINFO_END_OF_FILE_INFORMATION
:
737 info
->end_of_file_info
.in
.size
) == -1) {
738 return map_nt_error_from_unix(errno
);
741 case RAW_SFILEINFO_SETATTRE
:
742 unix_times
.actime
= info
->setattre
.in
.access_time
;
743 unix_times
.modtime
= info
->setattre
.in
.write_time
;
745 if (unix_times
.actime
== 0 && unix_times
.modtime
== 0) {
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
;
754 /* Set the date on this file */
755 if (svfs_file_utime(f
->fd
, &unix_times
) != 0) {
756 return NT_STATUS_ACCESS_DENIED
;
760 DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
761 info
->generic
.level
));
762 return NT_STATUS_NOT_IMPLEMENTED
;
769 return filesystem space info
771 static NTSTATUS
svfs_fsinfo(struct ntvfs_module_context
*ntvfs
,
772 struct ntvfs_request
*req
, union smb_fsinfo
*fs
)
774 struct svfs_private
*p
= ntvfs
->private_data
;
777 if (fs
->generic
.level
!= RAW_QFS_GENERIC
) {
778 return ntvfs_map_fsinfo(ntvfs
, req
, fs
);
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
);
787 fs
->generic
.out
.block_size
= 512;
789 if (stat(p
->connectpath
, &st
) != 0) {
790 return NT_STATUS_DISK_CORRUPT_ERROR
;
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
;
811 return filesystem attribute info
813 static NTSTATUS
svfs_fsattr(struct ntvfs_module_context
*ntvfs
,
814 struct ntvfs_request
*req
, union smb_fsattr
*fs
)
817 struct svfs_private
*p
= ntvfs
->private_data
;
819 if (fs
->generic
.level
!= RAW_FSATTR_GENERIC
) {
820 return ntvfs_map_fsattr(ntvfs
, req
, fs
);
823 if (stat(p
->connectpath
, &st
) == -1) {
824 return map_nt_error_from_unix(errno
);
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 lpcfg_servicename(req
->tcon
->service
));
843 return print queue info
845 static NTSTATUS
svfs_lpq(struct ntvfs_module_context
*ntvfs
,
846 struct ntvfs_request
*req
, union smb_lpq
*lpq
)
848 return NT_STATUS_NOT_SUPPORTED
;
852 list files in a directory matching a wildcard pattern
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
*))
859 struct svfs_dir
*dir
;
861 struct svfs_private
*p
= ntvfs
->private_data
;
862 struct search_state
*search
;
863 union smb_search_data file
;
864 unsigned int max_count
;
866 if (io
->generic
.level
!= RAW_SEARCH_TRANS2
) {
867 return NT_STATUS_NOT_SUPPORTED
;
870 if (io
->generic
.data_level
!= RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
) {
871 return NT_STATUS_NOT_SUPPORTED
;
874 search
= talloc_zero(p
, struct search_state
);
876 return NT_STATUS_NO_MEMORY
;
879 max_count
= io
->t2ffirst
.in
.max_count
;
881 dir
= svfs_list(ntvfs
, req
, io
->t2ffirst
.in
.pattern
);
883 return NT_STATUS_FOOBAR
;
886 search
->handle
= p
->next_search_handle
;
889 if (dir
->count
< max_count
) {
890 max_count
= dir
->count
;
893 for (i
=0; i
< max_count
;i
++) {
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
);
904 if (!callback(search_private
, &file
)) {
909 search
->current_index
= i
;
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;
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
))) {
920 p
->next_search_handle
++;
921 DLIST_ADD(p
->search
, search
);
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
*))
933 struct svfs_dir
*dir
;
935 struct svfs_private
*p
= ntvfs
->private_data
;
936 struct search_state
*search
;
937 union smb_search_data file
;
938 unsigned int max_count
;
940 if (io
->generic
.level
!= RAW_SEARCH_TRANS2
) {
941 return NT_STATUS_NOT_SUPPORTED
;
944 if (io
->generic
.data_level
!= RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
) {
945 return NT_STATUS_NOT_SUPPORTED
;
948 for (search
=p
->search
; search
; search
= search
->next
) {
949 if (search
->handle
== io
->t2fnext
.in
.handle
) break;
953 /* we didn't find the search handle */
954 return NT_STATUS_FOOBAR
;
959 /* the client might be asking for something other than just continuing
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
;
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
;
982 max_count
= search
->current_index
+ io
->t2fnext
.in
.max_count
;
984 if (max_count
> dir
->count
) {
985 max_count
= dir
->count
;
988 for (i
= search
->current_index
; i
< max_count
;i
++) {
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
);
999 if (!callback(search_private
, &file
)) {
1004 io
->t2fnext
.out
.count
= i
- search
->current_index
;
1005 io
->t2fnext
.out
.end_of_search
= (i
== dir
->count
) ? 1 : 0;
1007 search
->current_index
= i
;
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
);
1016 return NT_STATUS_OK
;
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
)
1023 struct svfs_private
*p
= ntvfs
->private_data
;
1024 struct search_state
*search
;
1026 for (search
=p
->search
; search
; search
= search
->next
) {
1027 if (search
->handle
== io
->findclose
.in
.handle
) break;
1031 /* we didn't find the search handle */
1032 return NT_STATUS_FOOBAR
;
1035 DLIST_REMOVE(p
->search
, search
);
1036 talloc_free(search
);
1038 return NT_STATUS_OK
;
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
)
1045 return NT_STATUS_ACCESS_DENIED
;
1050 initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
1052 NTSTATUS
ntvfs_simple_init(void)
1055 struct ntvfs_ops ops
;
1056 NTVFS_CURRENT_CRITICAL_SIZES(vers
);
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
;
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
;
1092 /* register ourselves with the NTVFS subsystem. We register
1093 under names 'simple'
1096 ops
.type
= NTVFS_DISK
;
1097 ops
.name
= "simple";
1098 ret
= ntvfs_register(&ops
, &vers
);
1100 if (!NT_STATUS_IS_OK(ret
)) {
1101 DEBUG(0,("Failed to register simple backend with name: %s!\n",