2 Unix SMB/CIFS implementation.
4 simpler Samba VFS filesystem backend for clients which support the
5 CIFS Unix Extensions or newer CIFS POSIX protocol extensions
7 Copyright (C) Andrew Tridgell 2003
8 Copyright (C) Steve French 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 this implements a very simple NTVFS filesystem backend.
25 this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
26 minimal work to give a working backend.
30 #include "system/dir.h"
31 #include "system/filesys.h"
32 #include "cifsposix.h"
33 #include "system/time.h"
34 #include "../lib/util/dlinklist.h"
35 #include "ntvfs/ntvfs.h"
36 #include "ntvfs/cifs_posix_cli/proto.h"
42 #define CHECK_READ_ONLY(req) do { if (share_bool_option(ntvfs->ctx->config, SHARE_READONLY, true)) return NT_STATUS_ACCESS_DENIED; } while (0)
45 connect to a share - used when a tree_connect operation comes
46 in. For a disk based backend we needs to ensure that the base
47 directory exists (tho it doesn't need to be accessible by the user,
50 static NTSTATUS
cifspsx_connect(struct ntvfs_module_context
*ntvfs
,
51 struct ntvfs_request
*req
,
55 struct cifspsx_private
*p
;
56 struct share_config
*scfg
= ntvfs
->ctx
->config
;
57 const char *sharename
;
59 switch (tcon
->generic
.level
) {
61 sharename
= tcon
->tcon
.in
.service
;
64 sharename
= tcon
->tconx
.in
.path
;
67 sharename
= tcon
->smb2
.in
.path
;
70 return NT_STATUS_INVALID_LEVEL
;
73 if (strncmp(sharename
, "\\\\", 2) == 0) {
74 char *str
= strchr(sharename
+2, '\\');
80 p
= talloc(ntvfs
, struct cifspsx_private
);
81 NT_STATUS_HAVE_NO_MEMORY(p
);
83 p
->next_search_handle
= 0;
84 p
->connectpath
= share_string_option(p
, scfg
, SHARE_PATH
, "");
88 /* the directory must exist */
89 if (stat(p
->connectpath
, &st
) != 0 || !S_ISDIR(st
.st_mode
)) {
90 DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n",
91 p
->connectpath
, sharename
));
92 return NT_STATUS_BAD_NETWORK_NAME
;
95 ntvfs
->ctx
->fs_type
= talloc_strdup(ntvfs
->ctx
, "NTFS");
96 NT_STATUS_HAVE_NO_MEMORY(ntvfs
->ctx
->fs_type
);
97 ntvfs
->ctx
->dev_type
= talloc_strdup(ntvfs
->ctx
, "A:");
98 NT_STATUS_HAVE_NO_MEMORY(ntvfs
->ctx
->dev_type
);
100 if (tcon
->generic
.level
== RAW_TCON_TCONX
) {
101 tcon
->tconx
.out
.fs_type
= ntvfs
->ctx
->fs_type
;
102 tcon
->tconx
.out
.dev_type
= ntvfs
->ctx
->dev_type
;
105 ntvfs
->private_data
= p
;
107 DEBUG(0,("WARNING: ntvfs cifs posix: connect to share [%s] with ROOT privileges!!!\n",sharename
));
113 disconnect from a share
115 static NTSTATUS
cifspsx_disconnect(struct ntvfs_module_context
*ntvfs
)
121 find open file handle given fd
123 static struct cifspsx_file
*find_fd(struct cifspsx_private
*cp
, struct ntvfs_handle
*handle
)
125 struct cifspsx_file
*f
;
128 p
= ntvfs_handle_get_backend_data(handle
, cp
->ntvfs
);
131 f
= talloc_get_type(p
, struct cifspsx_file
);
138 delete a file - the dirtype specifies the file types to include in the search.
139 The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
141 static NTSTATUS
cifspsx_unlink(struct ntvfs_module_context
*ntvfs
,
142 struct ntvfs_request
*req
,
143 union smb_unlink
*unl
)
147 CHECK_READ_ONLY(req
);
149 unix_path
= cifspsx_unix_path(ntvfs
, req
, unl
->unlink
.in
.pattern
);
151 /* ignoring wildcards ... */
152 if (unlink(unix_path
) == -1) {
153 return map_nt_error_from_unix_common(errno
);
161 ioctl interface - we don't do any
163 static NTSTATUS
cifspsx_ioctl(struct ntvfs_module_context
*ntvfs
,
164 struct ntvfs_request
*req
, union smb_ioctl
*io
)
166 return NT_STATUS_INVALID_PARAMETER
;
170 check if a directory exists
172 static NTSTATUS
cifspsx_chkpath(struct ntvfs_module_context
*ntvfs
,
173 struct ntvfs_request
*req
,
174 union smb_chkpath
*cp
)
179 unix_path
= cifspsx_unix_path(ntvfs
, req
, cp
->chkpath
.in
.path
);
181 if (stat(unix_path
, &st
) == -1) {
182 return map_nt_error_from_unix_common(errno
);
185 if (!S_ISDIR(st
.st_mode
)) {
186 return NT_STATUS_NOT_A_DIRECTORY
;
193 build a file_id from a stat struct
195 static uint64_t cifspsx_file_id(struct stat
*st
)
197 uint64_t ret
= st
->st_ino
;
204 approximately map a struct stat to a generic fileinfo struct
206 static NTSTATUS
cifspsx_map_fileinfo(struct ntvfs_module_context
*ntvfs
,
207 struct ntvfs_request
*req
, union smb_fileinfo
*info
,
208 struct stat
*st
, const char *unix_path
)
210 struct cifspsx_dir
*dir
= NULL
;
211 char *pattern
= NULL
;
213 const char *s
, *short_name
;
215 s
= strrchr(unix_path
, '/');
222 asprintf(&pattern
, "%s:*", unix_path
);
225 dir
= cifspsx_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
= cifspsx_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
= cifspsx_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
cifspsx_qpathinfo(struct ntvfs_module_context
*ntvfs
,
283 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
288 DEBUG(19,("cifspsx_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
= cifspsx_unix_path(ntvfs
, req
, info
->generic
.in
.file
.path
);
294 DEBUG(19,("cifspsx_qpathinfo: file %s\n", unix_path
));
295 if (stat(unix_path
, &st
) == -1) {
296 DEBUG(19,("cifspsx_qpathinfo: file %s errno=%d\n", unix_path
, errno
));
297 return map_nt_error_from_unix_common(errno
);
299 DEBUG(19,("cifspsx_qpathinfo: file %s, stat done\n", unix_path
));
300 return cifspsx_map_fileinfo(ntvfs
, req
, info
, &st
, unix_path
);
304 query info on a open file
306 static NTSTATUS
cifspsx_qfileinfo(struct ntvfs_module_context
*ntvfs
,
307 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
309 struct cifspsx_private
*p
= ntvfs
->private_data
;
310 struct cifspsx_file
*f
;
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 cifspsx_map_fileinfo(ntvfs
, req
,info
, &st
, f
->name
);
333 static NTSTATUS
cifspsx_open(struct ntvfs_module_context
*ntvfs
,
334 struct ntvfs_request
*req
, union smb_open
*io
)
336 struct cifspsx_private
*p
= ntvfs
->private_data
;
340 struct cifspsx_file
*f
;
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
= cifspsx_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,("cifspsx_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,("cifspsx_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,("cifspsx_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 cifspsx_file
);
420 NT_STATUS_HAVE_NO_MEMORY(f
);
422 f
->name
= talloc_strdup(f
, unix_path
);
423 NT_STATUS_HAVE_NO_MEMORY(f
->name
);
425 DLIST_ADD(p
->open_files
, f
);
427 status
= ntvfs_handle_set_backend_data(handle
, ntvfs
, f
);
428 NT_STATUS_NOT_OK_RETURN(status
);
430 ZERO_STRUCT(io
->generic
.out
);
432 unix_to_nt_time(&io
->generic
.out
.create_time
, st
.st_ctime
);
433 unix_to_nt_time(&io
->generic
.out
.access_time
, st
.st_atime
);
434 unix_to_nt_time(&io
->generic
.out
.write_time
, st
.st_mtime
);
435 unix_to_nt_time(&io
->generic
.out
.change_time
, st
.st_mtime
);
436 io
->generic
.out
.file
.ntvfs
= handle
;
437 io
->generic
.out
.alloc_size
= st
.st_size
;
438 io
->generic
.out
.size
= st
.st_size
;
439 io
->generic
.out
.attrib
= cifspsx_unix_to_dos_attrib(st
.st_mode
);
440 io
->generic
.out
.is_directory
= S_ISDIR(st
.st_mode
) ? 1 : 0;
448 static NTSTATUS
cifspsx_mkdir(struct ntvfs_module_context
*ntvfs
,
449 struct ntvfs_request
*req
, union smb_mkdir
*md
)
453 CHECK_READ_ONLY(req
);
455 if (md
->generic
.level
!= RAW_MKDIR_MKDIR
) {
456 return NT_STATUS_INVALID_LEVEL
;
459 unix_path
= cifspsx_unix_path(ntvfs
, req
, md
->mkdir
.in
.path
);
461 if (mkdir(unix_path
, 0777) == -1) {
462 return map_nt_error_from_unix_common(errno
);
471 static NTSTATUS
cifspsx_rmdir(struct ntvfs_module_context
*ntvfs
,
472 struct ntvfs_request
*req
, struct smb_rmdir
*rd
)
476 CHECK_READ_ONLY(req
);
478 unix_path
= cifspsx_unix_path(ntvfs
, req
, rd
->in
.path
);
480 if (rmdir(unix_path
) == -1) {
481 return map_nt_error_from_unix_common(errno
);
488 rename a set of files
490 static NTSTATUS
cifspsx_rename(struct ntvfs_module_context
*ntvfs
,
491 struct ntvfs_request
*req
, union smb_rename
*ren
)
493 char *unix_path1
, *unix_path2
;
495 CHECK_READ_ONLY(req
);
497 if (ren
->generic
.level
!= RAW_RENAME_RENAME
) {
498 return NT_STATUS_INVALID_LEVEL
;
501 unix_path1
= cifspsx_unix_path(ntvfs
, req
, ren
->rename
.in
.pattern1
);
502 unix_path2
= cifspsx_unix_path(ntvfs
, req
, ren
->rename
.in
.pattern2
);
504 if (rename(unix_path1
, unix_path2
) == -1) {
505 return map_nt_error_from_unix_common(errno
);
514 static NTSTATUS
cifspsx_copy(struct ntvfs_module_context
*ntvfs
,
515 struct ntvfs_request
*req
, struct smb_copy
*cp
)
517 return NT_STATUS_NOT_SUPPORTED
;
523 static NTSTATUS
cifspsx_read(struct ntvfs_module_context
*ntvfs
,
524 struct ntvfs_request
*req
, union smb_read
*rd
)
526 struct cifspsx_private
*p
= ntvfs
->private_data
;
527 struct cifspsx_file
*f
;
530 if (rd
->generic
.level
!= RAW_READ_READX
) {
531 return NT_STATUS_NOT_SUPPORTED
;
534 f
= find_fd(p
, rd
->readx
.in
.file
.ntvfs
);
536 return NT_STATUS_INVALID_HANDLE
;
542 rd
->readx
.in
.offset
);
544 return map_nt_error_from_unix_common(errno
);
547 rd
->readx
.out
.nread
= ret
;
548 rd
->readx
.out
.remaining
= 0; /* should fill this in? */
549 rd
->readx
.out
.compaction_mode
= 0;
557 static NTSTATUS
cifspsx_write(struct ntvfs_module_context
*ntvfs
,
558 struct ntvfs_request
*req
, union smb_write
*wr
)
560 struct cifspsx_private
*p
= ntvfs
->private_data
;
561 struct cifspsx_file
*f
;
564 if (wr
->generic
.level
!= RAW_WRITE_WRITEX
) {
565 return ntvfs_map_write(ntvfs
, req
, wr
);
568 CHECK_READ_ONLY(req
);
570 f
= find_fd(p
, wr
->writex
.in
.file
.ntvfs
);
572 return NT_STATUS_INVALID_HANDLE
;
578 wr
->writex
.in
.offset
);
580 return map_nt_error_from_unix_common(errno
);
583 wr
->writex
.out
.nwritten
= ret
;
584 wr
->writex
.out
.remaining
= 0; /* should fill this in? */
592 static NTSTATUS
cifspsx_seek(struct ntvfs_module_context
*ntvfs
,
593 struct ntvfs_request
*req
,
596 return NT_STATUS_NOT_SUPPORTED
;
602 static NTSTATUS
cifspsx_flush(struct ntvfs_module_context
*ntvfs
,
603 struct ntvfs_request
*req
,
606 struct cifspsx_private
*p
= ntvfs
->private_data
;
607 struct cifspsx_file
*f
;
609 switch (io
->generic
.level
) {
610 case RAW_FLUSH_FLUSH
:
612 /* ignore the additional unknown option in SMB2 */
613 f
= find_fd(p
, io
->generic
.in
.file
.ntvfs
);
615 return NT_STATUS_INVALID_HANDLE
;
621 for (f
=p
->open_files
;f
;f
=f
->next
) {
627 return NT_STATUS_INVALID_LEVEL
;
633 static NTSTATUS
cifspsx_close(struct ntvfs_module_context
*ntvfs
,
634 struct ntvfs_request
*req
,
637 struct cifspsx_private
*p
= ntvfs
->private_data
;
638 struct cifspsx_file
*f
;
640 if (io
->generic
.level
!= RAW_CLOSE_CLOSE
) {
641 /* we need a mapping function */
642 return NT_STATUS_INVALID_LEVEL
;
645 f
= find_fd(p
, io
->close
.in
.file
.ntvfs
);
647 return NT_STATUS_INVALID_HANDLE
;
650 if (close(f
->fd
) == -1) {
651 return map_nt_error_from_unix_common(errno
);
654 DLIST_REMOVE(p
->open_files
, f
);
655 talloc_free(f
->name
);
664 static NTSTATUS
cifspsx_exit(struct ntvfs_module_context
*ntvfs
,
665 struct ntvfs_request
*req
)
667 return NT_STATUS_NOT_SUPPORTED
;
671 logoff - closing files
673 static NTSTATUS
cifspsx_logoff(struct ntvfs_module_context
*ntvfs
,
674 struct ntvfs_request
*req
)
676 return NT_STATUS_NOT_SUPPORTED
;
680 setup for an async call
682 static NTSTATUS
cifspsx_async_setup(struct ntvfs_module_context
*ntvfs
,
683 struct ntvfs_request
*req
,
692 static NTSTATUS
cifspsx_cancel(struct ntvfs_module_context
*ntvfs
, struct ntvfs_request
*req
)
694 return NT_STATUS_UNSUCCESSFUL
;
700 static NTSTATUS
cifspsx_lock(struct ntvfs_module_context
*ntvfs
,
701 struct ntvfs_request
*req
, union smb_lock
*lck
)
703 DEBUG(0,("REWRITE: not doing byte range locking!\n"));
708 set info on a pathname
710 static NTSTATUS
cifspsx_setpathinfo(struct ntvfs_module_context
*ntvfs
,
711 struct ntvfs_request
*req
, union smb_setfileinfo
*st
)
713 CHECK_READ_ONLY(req
);
715 return NT_STATUS_NOT_SUPPORTED
;
719 set info on a open file
721 static NTSTATUS
cifspsx_setfileinfo(struct ntvfs_module_context
*ntvfs
,
722 struct ntvfs_request
*req
,
723 union smb_setfileinfo
*info
)
725 struct cifspsx_private
*p
= ntvfs
->private_data
;
726 struct cifspsx_file
*f
;
727 struct utimbuf unix_times
;
729 CHECK_READ_ONLY(req
);
731 f
= find_fd(p
, info
->generic
.in
.file
.ntvfs
);
733 return NT_STATUS_INVALID_HANDLE
;
736 switch (info
->generic
.level
) {
737 case RAW_SFILEINFO_END_OF_FILE_INFO
:
738 case RAW_SFILEINFO_END_OF_FILE_INFORMATION
:
740 info
->end_of_file_info
.in
.size
) == -1) {
741 return map_nt_error_from_unix_common(errno
);
744 case RAW_SFILEINFO_SETATTRE
:
745 unix_times
.actime
= info
->setattre
.in
.access_time
;
746 unix_times
.modtime
= info
->setattre
.in
.write_time
;
748 if (unix_times
.actime
== 0 && unix_times
.modtime
== 0) {
752 /* set modify time = to access time if modify time was 0 */
753 if (unix_times
.actime
!= 0 && unix_times
.modtime
== 0) {
754 unix_times
.modtime
= unix_times
.actime
;
757 /* Set the date on this file */
758 if (cifspsx_file_utime(f
->fd
, &unix_times
) != 0) {
759 return NT_STATUS_ACCESS_DENIED
;
763 DEBUG(2,("cifspsx_setfileinfo: level %d not implemented\n",
764 info
->generic
.level
));
765 return NT_STATUS_NOT_IMPLEMENTED
;
772 return filesystem space info
774 static NTSTATUS
cifspsx_fsinfo(struct ntvfs_module_context
*ntvfs
,
775 struct ntvfs_request
*req
, union smb_fsinfo
*fs
)
777 struct cifspsx_private
*p
= ntvfs
->private_data
;
780 if (fs
->generic
.level
!= RAW_QFS_GENERIC
) {
781 return ntvfs_map_fsinfo(ntvfs
, req
, fs
);
784 if (sys_fsusage(p
->connectpath
,
785 &fs
->generic
.out
.blocks_free
,
786 &fs
->generic
.out
.blocks_total
) == -1) {
787 return map_nt_error_from_unix_common(errno
);
790 fs
->generic
.out
.block_size
= 512;
792 if (stat(p
->connectpath
, &st
) != 0) {
793 return NT_STATUS_DISK_CORRUPT_ERROR
;
796 fs
->generic
.out
.fs_id
= st
.st_ino
;
797 unix_to_nt_time(&fs
->generic
.out
.create_time
, st
.st_ctime
);
798 fs
->generic
.out
.serial_number
= st
.st_ino
;
799 fs
->generic
.out
.fs_attr
= 0;
800 fs
->generic
.out
.max_file_component_length
= 255;
801 fs
->generic
.out
.device_type
= 0;
802 fs
->generic
.out
.device_characteristics
= 0;
803 fs
->generic
.out
.quota_soft
= 0;
804 fs
->generic
.out
.quota_hard
= 0;
805 fs
->generic
.out
.quota_flags
= 0;
806 fs
->generic
.out
.volume_name
= talloc_strdup(req
, ntvfs
->ctx
->config
->name
);
807 fs
->generic
.out
.fs_type
= ntvfs
->ctx
->fs_type
;
814 return filesystem attribute info
816 static NTSTATUS
cifspsx_fsattr(struct ntvfs_module_context
*ntvfs
,
817 struct ntvfs_request
*req
, union smb_fsattr
*fs
)
820 struct cifspsx_private
*p
= ntvfs
->private_data
;
822 if (fs
->generic
.level
!= RAW_FSATTR_GENERIC
) {
823 return ntvfs_map_fsattr(ntvfs
, req
, fs
);
826 if (stat(p
->connectpath
, &st
) == -1) {
827 return map_nt_error_from_unix_common(errno
);
830 unix_to_nt_time(&fs
->generic
.out
.create_time
, st
.st_ctime
);
831 fs
->generic
.out
.fs_attr
=
832 FILE_CASE_PRESERVED_NAMES
|
833 FILE_CASE_SENSITIVE_SEARCH
|
834 FILE_PERSISTENT_ACLS
;
835 fs
->generic
.out
.max_file_component_length
= 255;
836 fs
->generic
.out
.serial_number
= 1;
837 fs
->generic
.out
.fs_type
= talloc_strdup(req
, "NTFS");
838 fs
->generic
.out
.volume_name
= talloc_strdup(req
,
839 lpcfg_servicename(req
->tcon
->service
));
846 return print queue info
848 static NTSTATUS
cifspsx_lpq(struct ntvfs_module_context
*ntvfs
,
849 struct ntvfs_request
*req
, union smb_lpq
*lpq
)
851 return NT_STATUS_NOT_SUPPORTED
;
855 list files in a directory matching a wildcard pattern
857 static NTSTATUS
cifspsx_search_first(struct ntvfs_module_context
*ntvfs
,
858 struct ntvfs_request
*req
, union smb_search_first
*io
,
859 void *search_private
,
860 bool (*callback
)(void *, const union smb_search_data
*))
862 struct cifspsx_dir
*dir
;
864 struct cifspsx_private
*p
= ntvfs
->private_data
;
865 struct search_state
*search
;
866 union smb_search_data file
;
867 unsigned int max_count
;
869 if (io
->generic
.level
!= RAW_SEARCH_TRANS2
) {
870 return NT_STATUS_NOT_SUPPORTED
;
873 if (io
->generic
.data_level
!= RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
) {
874 return NT_STATUS_NOT_SUPPORTED
;
877 search
= talloc_zero(p
, struct search_state
);
879 return NT_STATUS_NO_MEMORY
;
882 max_count
= io
->t2ffirst
.in
.max_count
;
884 dir
= cifspsx_list(ntvfs
, req
, io
->t2ffirst
.in
.pattern
);
886 return NT_STATUS_FOOBAR
;
889 search
->handle
= p
->next_search_handle
;
892 if (dir
->count
< max_count
) {
893 max_count
= dir
->count
;
896 for (i
=0; i
< max_count
;i
++) {
898 unix_to_nt_time(&file
.both_directory_info
.create_time
, dir
->files
[i
].st
.st_ctime
);
899 unix_to_nt_time(&file
.both_directory_info
.access_time
, dir
->files
[i
].st
.st_atime
);
900 unix_to_nt_time(&file
.both_directory_info
.write_time
, dir
->files
[i
].st
.st_mtime
);
901 unix_to_nt_time(&file
.both_directory_info
.change_time
, dir
->files
[i
].st
.st_mtime
);
902 file
.both_directory_info
.name
.s
= dir
->files
[i
].name
;
903 file
.both_directory_info
.short_name
.s
= dir
->files
[i
].name
;
904 file
.both_directory_info
.size
= dir
->files
[i
].st
.st_size
;
905 file
.both_directory_info
.attrib
= cifspsx_unix_to_dos_attrib(dir
->files
[i
].st
.st_mode
);
907 if (!callback(search_private
, &file
)) {
912 search
->current_index
= i
;
914 io
->t2ffirst
.out
.count
= i
;
915 io
->t2ffirst
.out
.handle
= search
->handle
;
916 io
->t2ffirst
.out
.end_of_search
= (i
== dir
->count
) ? 1 : 0;
918 /* work out if we are going to keep the search state */
919 if ((io
->t2ffirst
.in
.flags
& FLAG_TRANS2_FIND_CLOSE
) ||
920 ((io
->t2ffirst
.in
.flags
& FLAG_TRANS2_FIND_CLOSE_IF_END
) && (i
== dir
->count
))) {
923 p
->next_search_handle
++;
924 DLIST_ADD(p
->search
, search
);
930 /* continue a search */
931 static NTSTATUS
cifspsx_search_next(struct ntvfs_module_context
*ntvfs
,
932 struct ntvfs_request
*req
, union smb_search_next
*io
,
933 void *search_private
,
934 bool (*callback
)(void *, const union smb_search_data
*))
936 struct cifspsx_dir
*dir
;
938 struct cifspsx_private
*p
= ntvfs
->private_data
;
939 struct search_state
*search
;
940 union smb_search_data file
;
941 unsigned int max_count
;
943 if (io
->generic
.level
!= RAW_SEARCH_TRANS2
) {
944 return NT_STATUS_NOT_SUPPORTED
;
947 if (io
->generic
.data_level
!= RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
) {
948 return NT_STATUS_NOT_SUPPORTED
;
951 for (search
=p
->search
; search
; search
= search
->next
) {
952 if (search
->handle
== io
->t2fnext
.in
.handle
) break;
956 /* we didn't find the search handle */
957 return NT_STATUS_FOOBAR
;
962 /* the client might be asking for something other than just continuing
964 if (!(io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CONTINUE
) &&
965 (io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_REQUIRE_RESUME
) &&
966 io
->t2fnext
.in
.last_name
&& *io
->t2fnext
.in
.last_name
) {
967 /* look backwards first */
968 for (i
=search
->current_index
; i
> 0; i
--) {
969 if (strcmp(io
->t2fnext
.in
.last_name
, dir
->files
[i
-1].name
) == 0) {
970 search
->current_index
= i
;
975 /* then look forwards */
976 for (i
=search
->current_index
+1; i
<= dir
->count
; i
++) {
977 if (strcmp(io
->t2fnext
.in
.last_name
, dir
->files
[i
-1].name
) == 0) {
978 search
->current_index
= i
;
985 max_count
= search
->current_index
+ io
->t2fnext
.in
.max_count
;
987 if (max_count
> dir
->count
) {
988 max_count
= dir
->count
;
991 for (i
= search
->current_index
; i
< max_count
;i
++) {
993 unix_to_nt_time(&file
.both_directory_info
.create_time
, dir
->files
[i
].st
.st_ctime
);
994 unix_to_nt_time(&file
.both_directory_info
.access_time
, dir
->files
[i
].st
.st_atime
);
995 unix_to_nt_time(&file
.both_directory_info
.write_time
, dir
->files
[i
].st
.st_mtime
);
996 unix_to_nt_time(&file
.both_directory_info
.change_time
, dir
->files
[i
].st
.st_mtime
);
997 file
.both_directory_info
.name
.s
= dir
->files
[i
].name
;
998 file
.both_directory_info
.short_name
.s
= dir
->files
[i
].name
;
999 file
.both_directory_info
.size
= dir
->files
[i
].st
.st_size
;
1000 file
.both_directory_info
.attrib
= cifspsx_unix_to_dos_attrib(dir
->files
[i
].st
.st_mode
);
1002 if (!callback(search_private
, &file
)) {
1007 io
->t2fnext
.out
.count
= i
- search
->current_index
;
1008 io
->t2fnext
.out
.end_of_search
= (i
== dir
->count
) ? 1 : 0;
1010 search
->current_index
= i
;
1012 /* work out if we are going to keep the search state */
1013 if ((io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CLOSE
) ||
1014 ((io
->t2fnext
.in
.flags
& FLAG_TRANS2_FIND_CLOSE_IF_END
) && (i
== dir
->count
))) {
1015 DLIST_REMOVE(p
->search
, search
);
1016 talloc_free(search
);
1019 return NT_STATUS_OK
;
1022 /* close a search */
1023 static NTSTATUS
cifspsx_search_close(struct ntvfs_module_context
*ntvfs
,
1024 struct ntvfs_request
*req
, union smb_search_close
*io
)
1026 struct cifspsx_private
*p
= ntvfs
->private_data
;
1027 struct search_state
*search
;
1029 for (search
=p
->search
; search
; search
= search
->next
) {
1030 if (search
->handle
== io
->findclose
.in
.handle
) break;
1034 /* we didn't find the search handle */
1035 return NT_STATUS_FOOBAR
;
1038 DLIST_REMOVE(p
->search
, search
);
1039 talloc_free(search
);
1041 return NT_STATUS_OK
;
1044 /* SMBtrans - not used on file shares */
1045 static NTSTATUS
cifspsx_trans(struct ntvfs_module_context
*ntvfs
,
1046 struct ntvfs_request
*req
, struct smb_trans2
*trans2
)
1048 return NT_STATUS_ACCESS_DENIED
;
1053 initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
1055 NTSTATUS
ntvfs_cifs_posix_init(void)
1058 struct ntvfs_ops ops
;
1059 NTVFS_CURRENT_CRITICAL_SIZES(vers
);
1063 /* fill in all the operations */
1064 ops
.connect_fn
= cifspsx_connect
;
1065 ops
.disconnect_fn
= cifspsx_disconnect
;
1066 ops
.unlink_fn
= cifspsx_unlink
;
1067 ops
.chkpath_fn
= cifspsx_chkpath
;
1068 ops
.qpathinfo_fn
= cifspsx_qpathinfo
;
1069 ops
.setpathinfo_fn
= cifspsx_setpathinfo
;
1070 ops
.open_fn
= cifspsx_open
;
1071 ops
.mkdir_fn
= cifspsx_mkdir
;
1072 ops
.rmdir_fn
= cifspsx_rmdir
;
1073 ops
.rename_fn
= cifspsx_rename
;
1074 ops
.copy_fn
= cifspsx_copy
;
1075 ops
.ioctl_fn
= cifspsx_ioctl
;
1076 ops
.read_fn
= cifspsx_read
;
1077 ops
.write_fn
= cifspsx_write
;
1078 ops
.seek_fn
= cifspsx_seek
;
1079 ops
.flush_fn
= cifspsx_flush
;
1080 ops
.close_fn
= cifspsx_close
;
1081 ops
.exit_fn
= cifspsx_exit
;
1082 ops
.lock_fn
= cifspsx_lock
;
1083 ops
.setfileinfo_fn
= cifspsx_setfileinfo
;
1084 ops
.qfileinfo_fn
= cifspsx_qfileinfo
;
1085 ops
.fsinfo_fn
= cifspsx_fsinfo
;
1086 ops
.lpq_fn
= cifspsx_lpq
;
1087 ops
.search_first_fn
= cifspsx_search_first
;
1088 ops
.search_next_fn
= cifspsx_search_next
;
1089 ops
.search_close_fn
= cifspsx_search_close
;
1090 ops
.trans_fn
= cifspsx_trans
;
1091 ops
.logoff_fn
= cifspsx_logoff
;
1092 ops
.async_setup_fn
= cifspsx_async_setup
;
1093 ops
.cancel_fn
= cifspsx_cancel
;
1095 /* register ourselves with the NTVFS subsystem. We register
1096 under names 'cifsposix'
1099 ops
.type
= NTVFS_DISK
;
1100 ops
.name
= "cifsposix";
1101 ret
= ntvfs_register(&ops
, &vers
);
1103 if (!NT_STATUS_IS_OK(ret
)) {
1104 DEBUG(0,("Failed to register cifs posix backend with name: %s!\n",