notifyd: Use messaging_register for MSG_SMB_NOTIFY_REC_CHANGE
[Samba.git] / source4 / ntvfs / cifs_posix_cli / vfs_cifs_posix.c
blob83f02c0e8772433996d367c6b725f1f1be1bfd66
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 = share_string_option(p, 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, ret;
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 ret = asprintf(&pattern, "%s:*", unix_path);
223 if (ret == -1) {
224 return NT_STATUS_NO_MEMORY;
227 if (pattern) {
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;
279 return NT_STATUS_OK;
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)
288 char *unix_path;
289 struct stat st;
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;
314 struct stat st;
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);
321 if (!f) {
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);
334 open a file
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;
340 char *unix_path;
341 struct stat st;
342 int fd, flags;
343 struct cifspsx_file *f;
344 int create_flags, rdwr_flags;
345 bool readonly;
346 NTSTATUS status;
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);
354 if (readonly) {
355 create_flags = 0;
356 rdwr_flags = O_RDONLY;
357 } else {
358 create_flags = O_CREAT;
359 rdwr_flags = O_RDWR;
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;
368 break;
369 case NTCREATEX_DISP_OPEN:
370 case NTCREATEX_DISP_OVERWRITE:
371 flags = 0;
372 break;
373 case NTCREATEX_DISP_CREATE:
374 flags = create_flags | O_EXCL;
375 break;
376 case NTCREATEX_DISP_OPEN_IF:
377 flags = create_flags;
378 break;
379 default:
380 flags = 0;
381 break;
384 flags |= rdwr_flags;
386 if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
387 flags = O_RDONLY | O_DIRECTORY;
388 if (readonly) {
389 goto do_open;
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);
397 break;
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);
403 break;
407 do_open:
408 fd = open(unix_path, flags, 0644);
409 if (fd == -1) {
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));
415 close(fd);
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);
424 f->fd = fd;
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;
445 return NT_STATUS_OK;
449 create a directory
451 static NTSTATUS cifspsx_mkdir(struct ntvfs_module_context *ntvfs,
452 struct ntvfs_request *req, union smb_mkdir *md)
454 char *unix_path;
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);
468 return NT_STATUS_OK;
472 remove a directory
474 static NTSTATUS cifspsx_rmdir(struct ntvfs_module_context *ntvfs,
475 struct ntvfs_request *req, struct smb_rmdir *rd)
477 char *unix_path;
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);
487 return NT_STATUS_OK;
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);
511 return NT_STATUS_OK;
515 copy a set of files
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;
524 read from a file
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;
531 ssize_t ret;
533 if (rd->generic.level != RAW_READ_READX) {
534 return NT_STATUS_NOT_SUPPORTED;
537 f = find_fd(p, rd->readx.in.file.ntvfs);
538 if (!f) {
539 return NT_STATUS_INVALID_HANDLE;
542 ret = pread(f->fd,
543 rd->readx.out.data,
544 rd->readx.in.maxcnt,
545 rd->readx.in.offset);
546 if (ret == -1) {
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;
554 return NT_STATUS_OK;
558 write to a file
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;
565 ssize_t ret;
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);
574 if (!f) {
575 return NT_STATUS_INVALID_HANDLE;
578 ret = pwrite(f->fd,
579 wr->writex.in.data,
580 wr->writex.in.count,
581 wr->writex.in.offset);
582 if (ret == -1) {
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? */
589 return NT_STATUS_OK;
593 seek in a file
595 static NTSTATUS cifspsx_seek(struct ntvfs_module_context *ntvfs,
596 struct ntvfs_request *req,
597 union smb_seek *io)
599 return NT_STATUS_NOT_SUPPORTED;
603 flush a file
605 static NTSTATUS cifspsx_flush(struct ntvfs_module_context *ntvfs,
606 struct ntvfs_request *req,
607 union smb_flush *io)
609 struct cifspsx_private *p = ntvfs->private_data;
610 struct cifspsx_file *f;
612 switch (io->generic.level) {
613 case RAW_FLUSH_FLUSH:
614 case RAW_FLUSH_SMB2:
615 /* ignore the additional unknown option in SMB2 */
616 f = find_fd(p, io->generic.in.file.ntvfs);
617 if (!f) {
618 return NT_STATUS_INVALID_HANDLE;
620 fsync(f->fd);
621 return NT_STATUS_OK;
623 case RAW_FLUSH_ALL:
624 for (f=p->open_files;f;f=f->next) {
625 fsync(f->fd);
627 return NT_STATUS_OK;
630 return NT_STATUS_INVALID_LEVEL;
634 close a file
636 static NTSTATUS cifspsx_close(struct ntvfs_module_context *ntvfs,
637 struct ntvfs_request *req,
638 union smb_close *io)
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);
649 if (!f) {
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);
659 talloc_free(f);
661 return NT_STATUS_OK;
665 exit - closing files
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,
687 void *private_data)
689 return NT_STATUS_OK;
693 cancel an async call
695 static NTSTATUS cifspsx_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
697 return NT_STATUS_UNSUCCESSFUL;
701 lock a byte range
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"));
707 return NT_STATUS_OK;
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);
735 if (!f) {
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:
742 if (ftruncate(f->fd,
743 info->end_of_file_info.in.size) == -1) {
744 return map_nt_error_from_unix_common(errno);
746 break;
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) {
752 break;
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;
764 break;
765 default:
766 DEBUG(2,("cifspsx_setfileinfo: level %d not implemented\n",
767 info->generic.level));
768 return NT_STATUS_NOT_IMPLEMENTED;
770 return NT_STATUS_OK;
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;
781 struct stat st;
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;
812 return NT_STATUS_OK;
815 #if 0
817 return filesystem attribute info
819 static NTSTATUS cifspsx_fsattr(struct ntvfs_module_context *ntvfs,
820 struct ntvfs_request *req, union smb_fsattr *fs)
822 struct stat st;
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));
844 return NT_STATUS_OK;
846 #endif
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;
866 int i;
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);
881 if (!search) {
882 return NT_STATUS_NO_MEMORY;
885 max_count = io->t2ffirst.in.max_count;
887 dir = cifspsx_list(ntvfs, req, io->t2ffirst.in.pattern);
888 if (!dir) {
889 return NT_STATUS_FOOBAR;
892 search->handle = p->next_search_handle;
893 search->dir = dir;
895 if (dir->count < max_count) {
896 max_count = dir->count;
899 for (i=0; i < max_count;i++) {
900 ZERO_STRUCT(file);
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)) {
911 break;
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))) {
924 talloc_free(search);
925 } else {
926 p->next_search_handle++;
927 DLIST_ADD(p->search, search);
930 return NT_STATUS_OK;
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;
940 int i;
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;
958 if (!search) {
959 /* we didn't find the search handle */
960 return NT_STATUS_FOOBAR;
963 dir = search->dir;
965 /* the client might be asking for something other than just continuing
966 with the search */
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;
974 goto found;
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;
982 goto found;
987 found:
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++) {
995 ZERO_STRUCT(file);
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)) {
1006 break;
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;
1036 if (!search) {
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)
1060 NTSTATUS ret;
1061 struct ntvfs_ops ops;
1062 NTVFS_CURRENT_CRITICAL_SIZES(vers);
1064 ZERO_STRUCT(ops);
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",
1108 ops.name));
1111 return ret;