s4:ntvfs: add '_fn' suffix to all ntvfs_ops function pointers
[Samba/gebeck_regimport.git] / source4 / ntvfs / cifs_posix_cli / vfs_cifs_posix.c
blob8c5a53b6f8e8239a073a47ba40899f5b9559be1b
1 /*
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.
29 #include "includes.h"
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"
38 #ifndef O_DIRECTORY
39 #define O_DIRECTORY 0
40 #endif
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,
48 that comes later)
50 static NTSTATUS cifspsx_connect(struct ntvfs_module_context *ntvfs,
51 struct ntvfs_request *req,
52 union smb_tcon* tcon)
54 struct stat st;
55 struct cifspsx_private *p;
56 struct share_config *scfg = ntvfs->ctx->config;
57 const char *sharename;
59 switch (tcon->generic.level) {
60 case RAW_TCON_TCON:
61 sharename = tcon->tcon.in.service;
62 break;
63 case RAW_TCON_TCONX:
64 sharename = tcon->tconx.in.path;
65 break;
66 case RAW_TCON_SMB2:
67 sharename = tcon->smb2.in.path;
68 break;
69 default:
70 return NT_STATUS_INVALID_LEVEL;
73 if (strncmp(sharename, "\\\\", 2) == 0) {
74 char *str = strchr(sharename+2, '\\');
75 if (str) {
76 sharename = str + 1;
80 p = talloc(ntvfs, struct cifspsx_private);
81 NT_STATUS_HAVE_NO_MEMORY(p);
82 p->ntvfs = ntvfs;
83 p->next_search_handle = 0;
84 p->connectpath = talloc_strdup(p, share_string_option(scfg, SHARE_PATH, ""));
85 p->open_files = NULL;
86 p->search = NULL;
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));
109 return NT_STATUS_OK;
113 disconnect from a share
115 static NTSTATUS cifspsx_disconnect(struct ntvfs_module_context *ntvfs)
117 return NT_STATUS_OK;
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;
126 void *p;
128 p = ntvfs_handle_get_backend_data(handle, cp->ntvfs);
129 if (!p) return NULL;
131 f = talloc_get_type(p, struct cifspsx_file);
132 if (!f) return NULL;
134 return f;
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)
145 char *unix_path;
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);
156 return NT_STATUS_OK;
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)
176 char *unix_path;
177 struct stat st;
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;
189 return NT_STATUS_OK;
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;
198 ret <<= 32;
199 ret |= st->st_dev;
200 return ret;
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;
212 int i;
213 const char *s, *short_name;
215 s = strrchr(unix_path, '/');
216 if (s) {
217 short_name = s+1;
218 } else {
219 short_name = "";
222 asprintf(&pattern, "%s:*", unix_path);
224 if (pattern) {
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;
276 return NT_STATUS_OK;
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)
285 char *unix_path;
286 struct stat st;
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;
311 struct stat st;
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);
318 if (!f) {
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);
331 open a file
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;
337 char *unix_path;
338 struct stat st;
339 int fd, flags;
340 struct cifspsx_file *f;
341 int create_flags, rdwr_flags;
342 bool readonly;
343 NTSTATUS status;
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);
351 if (readonly) {
352 create_flags = 0;
353 rdwr_flags = O_RDONLY;
354 } else {
355 create_flags = O_CREAT;
356 rdwr_flags = O_RDWR;
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;
365 break;
366 case NTCREATEX_DISP_OPEN:
367 case NTCREATEX_DISP_OVERWRITE:
368 flags = 0;
369 break;
370 case NTCREATEX_DISP_CREATE:
371 flags = create_flags | O_EXCL;
372 break;
373 case NTCREATEX_DISP_OPEN_IF:
374 flags = create_flags;
375 break;
376 default:
377 flags = 0;
378 break;
381 flags |= rdwr_flags;
383 if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
384 flags = O_RDONLY | O_DIRECTORY;
385 if (readonly) {
386 goto do_open;
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);
394 break;
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);
400 break;
404 do_open:
405 fd = open(unix_path, flags, 0644);
406 if (fd == -1) {
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));
412 close(fd);
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);
421 f->fd = fd;
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;
442 return NT_STATUS_OK;
446 create a directory
448 static NTSTATUS cifspsx_mkdir(struct ntvfs_module_context *ntvfs,
449 struct ntvfs_request *req, union smb_mkdir *md)
451 char *unix_path;
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);
465 return NT_STATUS_OK;
469 remove a directory
471 static NTSTATUS cifspsx_rmdir(struct ntvfs_module_context *ntvfs,
472 struct ntvfs_request *req, struct smb_rmdir *rd)
474 char *unix_path;
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);
484 return NT_STATUS_OK;
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);
508 return NT_STATUS_OK;
512 copy a set of files
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;
521 read from a file
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;
528 ssize_t ret;
530 if (rd->generic.level != RAW_READ_READX) {
531 return NT_STATUS_NOT_SUPPORTED;
534 f = find_fd(p, rd->readx.in.file.ntvfs);
535 if (!f) {
536 return NT_STATUS_INVALID_HANDLE;
539 ret = pread(f->fd,
540 rd->readx.out.data,
541 rd->readx.in.maxcnt,
542 rd->readx.in.offset);
543 if (ret == -1) {
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;
551 return NT_STATUS_OK;
555 write to a file
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;
562 ssize_t ret;
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);
571 if (!f) {
572 return NT_STATUS_INVALID_HANDLE;
575 ret = pwrite(f->fd,
576 wr->writex.in.data,
577 wr->writex.in.count,
578 wr->writex.in.offset);
579 if (ret == -1) {
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? */
586 return NT_STATUS_OK;
590 seek in a file
592 static NTSTATUS cifspsx_seek(struct ntvfs_module_context *ntvfs,
593 struct ntvfs_request *req,
594 union smb_seek *io)
596 return NT_STATUS_NOT_SUPPORTED;
600 flush a file
602 static NTSTATUS cifspsx_flush(struct ntvfs_module_context *ntvfs,
603 struct ntvfs_request *req,
604 union smb_flush *io)
606 struct cifspsx_private *p = ntvfs->private_data;
607 struct cifspsx_file *f;
609 switch (io->generic.level) {
610 case RAW_FLUSH_FLUSH:
611 case RAW_FLUSH_SMB2:
612 /* ignore the additional unknown option in SMB2 */
613 f = find_fd(p, io->generic.in.file.ntvfs);
614 if (!f) {
615 return NT_STATUS_INVALID_HANDLE;
617 fsync(f->fd);
618 return NT_STATUS_OK;
620 case RAW_FLUSH_ALL:
621 for (f=p->open_files;f;f=f->next) {
622 fsync(f->fd);
624 return NT_STATUS_OK;
627 return NT_STATUS_INVALID_LEVEL;
631 close a file
633 static NTSTATUS cifspsx_close(struct ntvfs_module_context *ntvfs,
634 struct ntvfs_request *req,
635 union smb_close *io)
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);
646 if (!f) {
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);
656 talloc_free(f);
658 return NT_STATUS_OK;
662 exit - closing files
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,
684 void *private_data)
686 return NT_STATUS_OK;
690 cancel an async call
692 static NTSTATUS cifspsx_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
694 return NT_STATUS_UNSUCCESSFUL;
698 lock a byte range
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"));
704 return NT_STATUS_OK;
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);
732 if (!f) {
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:
739 if (ftruncate(f->fd,
740 info->end_of_file_info.in.size) == -1) {
741 return map_nt_error_from_unix_common(errno);
743 break;
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) {
749 break;
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;
761 break;
762 default:
763 DEBUG(2,("cifspsx_setfileinfo: level %d not implemented\n",
764 info->generic.level));
765 return NT_STATUS_NOT_IMPLEMENTED;
767 return NT_STATUS_OK;
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;
778 struct stat st;
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;
809 return NT_STATUS_OK;
812 #if 0
814 return filesystem attribute info
816 static NTSTATUS cifspsx_fsattr(struct ntvfs_module_context *ntvfs,
817 struct ntvfs_request *req, union smb_fsattr *fs)
819 struct stat st;
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));
841 return NT_STATUS_OK;
843 #endif
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;
863 int i;
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);
878 if (!search) {
879 return NT_STATUS_NO_MEMORY;
882 max_count = io->t2ffirst.in.max_count;
884 dir = cifspsx_list(ntvfs, req, io->t2ffirst.in.pattern);
885 if (!dir) {
886 return NT_STATUS_FOOBAR;
889 search->handle = p->next_search_handle;
890 search->dir = dir;
892 if (dir->count < max_count) {
893 max_count = dir->count;
896 for (i=0; i < max_count;i++) {
897 ZERO_STRUCT(file);
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)) {
908 break;
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))) {
921 talloc_free(search);
922 } else {
923 p->next_search_handle++;
924 DLIST_ADD(p->search, search);
927 return NT_STATUS_OK;
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;
937 int i;
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;
955 if (!search) {
956 /* we didn't find the search handle */
957 return NT_STATUS_FOOBAR;
960 dir = search->dir;
962 /* the client might be asking for something other than just continuing
963 with the search */
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;
971 goto found;
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;
979 goto found;
984 found:
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++) {
992 ZERO_STRUCT(file);
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)) {
1003 break;
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;
1033 if (!search) {
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)
1057 NTSTATUS ret;
1058 struct ntvfs_ops ops;
1059 NTVFS_CURRENT_CRITICAL_SIZES(vers);
1061 ZERO_STRUCT(ops);
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",
1105 ops.name));
1108 return ret;