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 ret
= asprintf(&pattern
, "%s:*", unix_path
);
224 return NT_STATUS_NO_MEMORY
;
228 dir
= cifspsx_list_unix(req
, req
, pattern
);
231 unix_to_nt_time(&info
->generic
.out
.create_time
, st
->st_ctime
);
232 unix_to_nt_time(&info
->generic
.out
.access_time
, st
->st_atime
);
233 unix_to_nt_time(&info
->generic
.out
.write_time
, st
->st_mtime
);
234 unix_to_nt_time(&info
->generic
.out
.change_time
, st
->st_mtime
);
235 info
->generic
.out
.alloc_size
= st
->st_size
;
236 info
->generic
.out
.size
= st
->st_size
;
237 info
->generic
.out
.attrib
= cifspsx_unix_to_dos_attrib(st
->st_mode
);
238 info
->generic
.out
.alloc_size
= st
->st_blksize
* st
->st_blocks
;
239 info
->generic
.out
.nlink
= st
->st_nlink
;
240 info
->generic
.out
.directory
= S_ISDIR(st
->st_mode
) ? 1 : 0;
241 info
->generic
.out
.file_id
= cifspsx_file_id(st
);
242 /* REWRITE: TODO stuff in here */
243 info
->generic
.out
.delete_pending
= 0;
244 info
->generic
.out
.ea_size
= 0;
245 info
->generic
.out
.num_eas
= 0;
246 info
->generic
.out
.fname
.s
= talloc_strdup(req
, short_name
);
247 info
->generic
.out
.alt_fname
.s
= talloc_strdup(req
, short_name
);
248 info
->generic
.out
.compressed_size
= 0;
249 info
->generic
.out
.format
= 0;
250 info
->generic
.out
.unit_shift
= 0;
251 info
->generic
.out
.chunk_shift
= 0;
252 info
->generic
.out
.cluster_shift
= 0;
254 info
->generic
.out
.access_flags
= 0;
255 info
->generic
.out
.position
= 0;
256 info
->generic
.out
.mode
= 0;
257 info
->generic
.out
.alignment_requirement
= 0;
258 info
->generic
.out
.reparse_tag
= 0;
259 info
->generic
.out
.num_streams
= 0;
260 /* setup a single data stream */
261 info
->generic
.out
.num_streams
= 1 + (dir
?dir
->count
:0);
262 info
->generic
.out
.streams
= talloc_array(req
,
263 struct stream_struct
,
264 info
->generic
.out
.num_streams
);
265 if (!info
->generic
.out
.streams
) {
266 return NT_STATUS_NO_MEMORY
;
268 info
->generic
.out
.streams
[0].size
= st
->st_size
;
269 info
->generic
.out
.streams
[0].alloc_size
= st
->st_size
;
270 info
->generic
.out
.streams
[0].stream_name
.s
= talloc_strdup(req
,"::$DATA");
272 for (i
=0;dir
&& i
<dir
->count
;i
++) {
273 s
= strchr(dir
->files
[i
].name
, ':');
274 info
->generic
.out
.streams
[1+i
].size
= dir
->files
[i
].st
.st_size
;
275 info
->generic
.out
.streams
[1+i
].alloc_size
= dir
->files
[i
].st
.st_size
;
276 info
->generic
.out
.streams
[1+i
].stream_name
.s
= s
?s
:dir
->files
[i
].name
;
283 return info on a pathname
285 static NTSTATUS
cifspsx_qpathinfo(struct ntvfs_module_context
*ntvfs
,
286 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
291 DEBUG(19,("cifspsx_qpathinfo: file %s level 0x%x\n", info
->generic
.in
.file
.path
, info
->generic
.level
));
292 if (info
->generic
.level
!= RAW_FILEINFO_GENERIC
) {
293 return ntvfs_map_qpathinfo(ntvfs
, req
, info
);
296 unix_path
= cifspsx_unix_path(ntvfs
, req
, info
->generic
.in
.file
.path
);
297 DEBUG(19,("cifspsx_qpathinfo: file %s\n", unix_path
));
298 if (stat(unix_path
, &st
) == -1) {
299 DEBUG(19,("cifspsx_qpathinfo: file %s errno=%d\n", unix_path
, errno
));
300 return map_nt_error_from_unix_common(errno
);
302 DEBUG(19,("cifspsx_qpathinfo: file %s, stat done\n", unix_path
));
303 return cifspsx_map_fileinfo(ntvfs
, req
, info
, &st
, unix_path
);
307 query info on a open file
309 static NTSTATUS
cifspsx_qfileinfo(struct ntvfs_module_context
*ntvfs
,
310 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
312 struct cifspsx_private
*p
= ntvfs
->private_data
;
313 struct cifspsx_file
*f
;
316 if (info
->generic
.level
!= RAW_FILEINFO_GENERIC
) {
317 return ntvfs_map_qfileinfo(ntvfs
, req
, info
);
320 f
= find_fd(p
, info
->generic
.in
.file
.ntvfs
);
322 return NT_STATUS_INVALID_HANDLE
;
325 if (fstat(f
->fd
, &st
) == -1) {
326 return map_nt_error_from_unix_common(errno
);
329 return cifspsx_map_fileinfo(ntvfs
, req
,info
, &st
, f
->name
);
336 static NTSTATUS
cifspsx_open(struct ntvfs_module_context
*ntvfs
,
337 struct ntvfs_request
*req
, union smb_open
*io
)
339 struct cifspsx_private
*p
= ntvfs
->private_data
;
343 struct cifspsx_file
*f
;
344 int create_flags
, rdwr_flags
;
347 struct ntvfs_handle
*handle
;
349 if (io
->generic
.level
!= RAW_OPEN_GENERIC
) {
350 return ntvfs_map_open(ntvfs
, req
, io
);
353 readonly
= share_bool_option(ntvfs
->ctx
->config
, SHARE_READONLY
, SHARE_READONLY_DEFAULT
);
356 rdwr_flags
= O_RDONLY
;
358 create_flags
= O_CREAT
;
362 unix_path
= cifspsx_unix_path(ntvfs
, req
, io
->ntcreatex
.in
.fname
);
364 switch (io
->generic
.in
.open_disposition
) {
365 case NTCREATEX_DISP_SUPERSEDE
:
366 case NTCREATEX_DISP_OVERWRITE_IF
:
367 flags
= create_flags
| O_TRUNC
;
369 case NTCREATEX_DISP_OPEN
:
370 case NTCREATEX_DISP_OVERWRITE
:
373 case NTCREATEX_DISP_CREATE
:
374 flags
= create_flags
| O_EXCL
;
376 case NTCREATEX_DISP_OPEN_IF
:
377 flags
= create_flags
;
386 if (io
->generic
.in
.create_options
& NTCREATEX_OPTIONS_DIRECTORY
) {
387 flags
= O_RDONLY
| O_DIRECTORY
;
391 switch (io
->generic
.in
.open_disposition
) {
392 case NTCREATEX_DISP_CREATE
:
393 if (mkdir(unix_path
, 0755) == -1) {
394 DEBUG(9,("cifspsx_open: mkdir %s errno=%d\n", unix_path
, errno
));
395 return map_nt_error_from_unix_common(errno
);
398 case NTCREATEX_DISP_OPEN_IF
:
399 if (mkdir(unix_path
, 0755) == -1 && errno
!= EEXIST
) {
400 DEBUG(9,("cifspsx_open: mkdir %s errno=%d\n", unix_path
, errno
));
401 return map_nt_error_from_unix_common(errno
);
408 fd
= open(unix_path
, flags
, 0644);
410 return map_nt_error_from_unix_common(errno
);
413 if (fstat(fd
, &st
) == -1) {
414 DEBUG(9,("cifspsx_open: fstat errno=%d\n", errno
));
416 return map_nt_error_from_unix_common(errno
);
419 status
= ntvfs_handle_new(ntvfs
, req
, &handle
);
420 NT_STATUS_NOT_OK_RETURN(status
);
422 f
= talloc(handle
, struct cifspsx_file
);
423 NT_STATUS_HAVE_NO_MEMORY(f
);
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
= cifspsx_unix_to_dos_attrib(st
.st_mode
);
443 io
->generic
.out
.is_directory
= S_ISDIR(st
.st_mode
) ? 1 : 0;
451 static NTSTATUS
cifspsx_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
= cifspsx_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
cifspsx_rmdir(struct ntvfs_module_context
*ntvfs
,
475 struct ntvfs_request
*req
, struct smb_rmdir
*rd
)
479 CHECK_READ_ONLY(req
);
481 unix_path
= cifspsx_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
cifspsx_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
= cifspsx_unix_path(ntvfs
, req
, ren
->rename
.in
.pattern1
);
505 unix_path2
= cifspsx_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
cifspsx_copy(struct ntvfs_module_context
*ntvfs
,
518 struct ntvfs_request
*req
, struct smb_copy
*cp
)
520 return NT_STATUS_NOT_SUPPORTED
;
526 static NTSTATUS
cifspsx_read(struct ntvfs_module_context
*ntvfs
,
527 struct ntvfs_request
*req
, union smb_read
*rd
)
529 struct cifspsx_private
*p
= ntvfs
->private_data
;
530 struct cifspsx_file
*f
;
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
cifspsx_write(struct ntvfs_module_context
*ntvfs
,
561 struct ntvfs_request
*req
, union smb_write
*wr
)
563 struct cifspsx_private
*p
= ntvfs
->private_data
;
564 struct cifspsx_file
*f
;
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
cifspsx_seek(struct ntvfs_module_context
*ntvfs
,
596 struct ntvfs_request
*req
,
599 return NT_STATUS_NOT_SUPPORTED
;
605 static NTSTATUS
cifspsx_flush(struct ntvfs_module_context
*ntvfs
,
606 struct ntvfs_request
*req
,
609 struct cifspsx_private
*p
= ntvfs
->private_data
;
610 struct cifspsx_file
*f
;
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
cifspsx_close(struct ntvfs_module_context
*ntvfs
,
637 struct ntvfs_request
*req
,
640 struct cifspsx_private
*p
= ntvfs
->private_data
;
641 struct cifspsx_file
*f
;
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
cifspsx_exit(struct ntvfs_module_context
*ntvfs
,
668 struct ntvfs_request
*req
)
670 return NT_STATUS_NOT_SUPPORTED
;
674 logoff - closing files
676 static NTSTATUS
cifspsx_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
cifspsx_async_setup(struct ntvfs_module_context
*ntvfs
,
686 struct ntvfs_request
*req
,
695 static NTSTATUS
cifspsx_cancel(struct ntvfs_module_context
*ntvfs
, struct ntvfs_request
*req
)
697 return NT_STATUS_UNSUCCESSFUL
;
703 static NTSTATUS
cifspsx_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
cifspsx_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
cifspsx_setfileinfo(struct ntvfs_module_context
*ntvfs
,
725 struct ntvfs_request
*req
,
726 union smb_setfileinfo
*info
)
728 struct cifspsx_private
*p
= ntvfs
->private_data
;
729 struct cifspsx_file
*f
;
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 (cifspsx_file_utime(f
->fd
, &unix_times
) != 0) {
762 return NT_STATUS_ACCESS_DENIED
;
766 DEBUG(2,("cifspsx_setfileinfo: level %d not implemented\n",
767 info
->generic
.level
));
768 return NT_STATUS_NOT_IMPLEMENTED
;
775 return filesystem space info
777 static NTSTATUS
cifspsx_fsinfo(struct ntvfs_module_context
*ntvfs
,
778 struct ntvfs_request
*req
, union smb_fsinfo
*fs
)
780 struct cifspsx_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
cifspsx_fsattr(struct ntvfs_module_context
*ntvfs
,
820 struct ntvfs_request
*req
, union smb_fsattr
*fs
)
823 struct cifspsx_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
cifspsx_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
cifspsx_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 cifspsx_dir
*dir
;
867 struct cifspsx_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
= cifspsx_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
= cifspsx_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
cifspsx_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 cifspsx_dir
*dir
;
941 struct cifspsx_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
= cifspsx_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
cifspsx_search_close(struct ntvfs_module_context
*ntvfs
,
1027 struct ntvfs_request
*req
, union smb_search_close
*io
)
1029 struct cifspsx_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
cifspsx_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_cifs_posix_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
= cifspsx_connect
;
1068 ops
.disconnect_fn
= cifspsx_disconnect
;
1069 ops
.unlink_fn
= cifspsx_unlink
;
1070 ops
.chkpath_fn
= cifspsx_chkpath
;
1071 ops
.qpathinfo_fn
= cifspsx_qpathinfo
;
1072 ops
.setpathinfo_fn
= cifspsx_setpathinfo
;
1073 ops
.open_fn
= cifspsx_open
;
1074 ops
.mkdir_fn
= cifspsx_mkdir
;
1075 ops
.rmdir_fn
= cifspsx_rmdir
;
1076 ops
.rename_fn
= cifspsx_rename
;
1077 ops
.copy_fn
= cifspsx_copy
;
1078 ops
.ioctl_fn
= cifspsx_ioctl
;
1079 ops
.read_fn
= cifspsx_read
;
1080 ops
.write_fn
= cifspsx_write
;
1081 ops
.seek_fn
= cifspsx_seek
;
1082 ops
.flush_fn
= cifspsx_flush
;
1083 ops
.close_fn
= cifspsx_close
;
1084 ops
.exit_fn
= cifspsx_exit
;
1085 ops
.lock_fn
= cifspsx_lock
;
1086 ops
.setfileinfo_fn
= cifspsx_setfileinfo
;
1087 ops
.qfileinfo_fn
= cifspsx_qfileinfo
;
1088 ops
.fsinfo_fn
= cifspsx_fsinfo
;
1089 ops
.lpq_fn
= cifspsx_lpq
;
1090 ops
.search_first_fn
= cifspsx_search_first
;
1091 ops
.search_next_fn
= cifspsx_search_next
;
1092 ops
.search_close_fn
= cifspsx_search_close
;
1093 ops
.trans_fn
= cifspsx_trans
;
1094 ops
.logoff_fn
= cifspsx_logoff
;
1095 ops
.async_setup_fn
= cifspsx_async_setup
;
1096 ops
.cancel_fn
= cifspsx_cancel
;
1098 /* register ourselves with the NTVFS subsystem. We register
1099 under names 'cifsposix'
1102 ops
.type
= NTVFS_DISK
;
1103 ops
.name
= "cifsposix";
1104 ret
= ntvfs_register(&ops
, &vers
);
1106 if (!NT_STATUS_IS_OK(ret
)) {
1107 DEBUG(0,("Failed to register cifs posix backend with name: %s!\n",