2 Unix SMB/Netbios implementation.
3 SMB client library implementation
4 Copyright (C) Andrew Tridgell 1998
5 Copyright (C) Richard Sharpe 2000, 2002
6 Copyright (C) John Terpstra 2000
7 Copyright (C) Tom Jansen (Ninja ISD) 2002
8 Copyright (C) Derrell Lipman 2003-2008
9 Copyright (C) Jeremy Allison 2007, 2008
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libsmb/libsmb.h"
27 #include "libsmbclient.h"
28 #include "libsmb_internal.h"
29 #include "../libcli/smb/smbXcli_base.h"
32 * Routine to open() a file ...
36 SMBC_open_ctx(SMBCCTX
*context
,
44 char *password
= NULL
;
45 char *workgroup
= NULL
;
47 char *targetpath
= NULL
;
48 struct cli_state
*targetcli
= NULL
;
50 SMBCFILE
*file
= NULL
;
53 NTSTATUS status
= NT_STATUS_OBJECT_PATH_INVALID
;
54 TALLOC_CTX
*frame
= talloc_stackframe();
56 if (!context
|| !context
->internal
->initialized
) {
57 errno
= EINVAL
; /* Best I can think of ... */
68 if (SMBC_parse_path(frame
,
84 if (!user
|| user
[0] == (char)0) {
85 user
= talloc_strdup(frame
, smbc_getUser(context
));
93 srv
= SMBC_server(frame
, context
, True
,
94 server
, port
, share
, &workgroup
, &user
, &password
);
96 if (errno
== EPERM
) errno
= EACCES
;
98 return NULL
; /* SMBC_server sets errno */
101 /* Hmmm, the test for a directory is suspect here ... FIXME */
103 if (strlen(path
) > 0 && path
[strlen(path
) - 1] == '\\') {
104 status
= NT_STATUS_OBJECT_PATH_INVALID
;
106 struct cli_credentials
*creds
= NULL
;
108 file
= SMB_MALLOC_P(SMBCFILE
);
117 creds
= context
->internal
->creds
;
118 /*d_printf(">>>open: resolving %s\n", path);*/
119 status
= cli_resolve_path(
122 srv
->cli
, path
, &targetcli
, &targetpath
);
123 if (!NT_STATUS_IS_OK(status
)) {
124 d_printf("Could not resolve %s\n", path
);
130 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
132 status
= cli_open(targetcli
, targetpath
, flags
,
133 context
->internal
->share_mode
, &fd
);
134 if (!NT_STATUS_IS_OK(status
)) {
136 /* Handle the error ... */
140 errno
= cli_status_to_errno(status
);
144 /* Fill in file struct */
147 file
->fname
= SMB_STRDUP(fname
);
152 * targetcli is either equal to srv->cli or
153 * is a subsidiary DFS connection. Either way
154 * file->cli_fd belongs to it so we must cache
155 * it for read/write/close, not re-resolve each time.
156 * Re-resolving is both slow and incorrect.
158 file
->targetcli
= targetcli
;
160 DLIST_ADD(context
->internal
->files
, file
);
163 * If the file was opened in O_APPEND mode, all write
164 * operations should be appended to the file. To do that,
165 * though, using this protocol, would require a getattrE()
166 * call for each and every write, to determine where the end
167 * of the file is. (There does not appear to be an append flag
168 * in the protocol.) Rather than add all of that overhead of
169 * retrieving the current end-of-file offset prior to each
170 * write operation, we'll assume that most append operations
171 * will continuously write, so we'll just set the offset to
172 * the end of the file now and hope that's adequate.
174 * Note to self: If this proves inadequate, and O_APPEND
175 * should, in some cases, be forced for each write, add a
176 * field in the context options structure, for
177 * "strict_append_mode" which would select between the current
178 * behavior (if FALSE) or issuing a getattrE() prior to each
179 * write and forcing the write to the end of the file (if
180 * TRUE). Adding that capability will likely require adding
181 * an "append" flag into the _SMBCFILE structure to track
182 * whether a file was opened in O_APPEND mode. -- djl
184 if (flags
& O_APPEND
) {
185 if (SMBC_lseek_ctx(context
, file
, 0, SEEK_END
) < 0) {
186 (void) SMBC_close_ctx(context
, file
);
197 /* Check if opendir needed ... */
199 if (!NT_STATUS_IS_OK(status
)) {
200 file
= smbc_getFunctionOpendir(context
)(context
, fname
);
203 errno
= cli_status_to_errno(status
);
208 errno
= EINVAL
; /* FIXME, correct errno ? */
214 * Routine to create a file
218 SMBC_creat_ctx(SMBCCTX
*context
,
222 if (!context
|| !context
->internal
->initialized
) {
227 return SMBC_open_ctx(context
, path
,
228 O_WRONLY
| O_CREAT
| O_TRUNC
, mode
);
232 * Routine to read() a file ...
236 SMBC_read_ctx(SMBCCTX
*context
,
242 TALLOC_CTX
*frame
= talloc_stackframe();
248 * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
249 * appears to pass file->offset (which is type off_t) differently than
250 * a local variable of type off_t. Using local variable "offset" in
251 * the call to cli_read() instead of file->offset fixes a problem
252 * retrieving data at an offset greater than 4GB.
256 if (!context
|| !context
->internal
->initialized
) {
262 DEBUG(4, ("smbc_read(%p, %zu)\n", file
, count
));
264 if (!SMBC_dlist_contains(context
->internal
->files
, file
)) {
270 offset
= file
->offset
;
272 /* Check that the buffer exists ... */
280 status
= cli_read(file
->targetcli
, file
->cli_fd
, (char *)buf
, offset
,
282 if (!NT_STATUS_IS_OK(status
)) {
284 errno
= cli_status_to_errno(status
);
290 DEBUG(4, (" --> %zu\n", ret
));
293 return ret
; /* Success, ret bytes of data ... */
297 SMBC_splice_ctx(SMBCCTX
*context
,
301 int (*splice_cb
)(off_t n
, void *priv
),
305 TALLOC_CTX
*frame
= talloc_stackframe();
308 if (!context
|| !context
->internal
->initialized
) {
314 if (!SMBC_dlist_contains(context
->internal
->files
, srcfile
)) {
320 if (!SMBC_dlist_contains(context
->internal
->files
, dstfile
)) {
326 status
= cli_splice(srcfile
->targetcli
, dstfile
->targetcli
,
327 srcfile
->cli_fd
, dstfile
->cli_fd
,
328 count
, srcfile
->offset
, dstfile
->offset
, &written
,
330 if (!NT_STATUS_IS_OK(status
)) {
332 errno
= cli_status_to_errno(status
);
336 srcfile
->offset
+= written
;
337 dstfile
->offset
+= written
;
344 * Routine to write() a file ...
348 SMBC_write_ctx(SMBCCTX
*context
,
354 TALLOC_CTX
*frame
= talloc_stackframe();
357 /* First check all pointers before dereferencing them */
359 if (!context
|| !context
->internal
->initialized
) {
365 if (!SMBC_dlist_contains(context
->internal
->files
, file
)) {
371 /* Check that the buffer exists ... */
379 offset
= file
->offset
; /* See "offset" comment in SMBC_read_ctx() */
381 status
= cli_writeall(file
->targetcli
, file
->cli_fd
,
382 0, (const uint8_t *)buf
, offset
, count
, NULL
);
383 if (!NT_STATUS_IS_OK(status
)) {
384 errno
= map_errno_from_nt_status(status
);
389 file
->offset
+= count
;
392 return count
; /* Success, 0 bytes of data ... */
396 * Routine to close() a file ...
400 SMBC_close_ctx(SMBCCTX
*context
,
403 TALLOC_CTX
*frame
= talloc_stackframe();
406 if (!context
|| !context
->internal
->initialized
) {
412 if (!SMBC_dlist_contains(context
->internal
->files
, file
)) {
421 return smbc_getFunctionClosedir(context
)(context
, file
);
424 status
= cli_close(file
->targetcli
, file
->cli_fd
);
425 if (!NT_STATUS_IS_OK(status
)) {
427 DEBUG(3, ("cli_close failed on %s. purging server.\n",
429 /* Deallocate slot and remove the server
430 * from the server cache if unused */
432 DLIST_REMOVE(context
->internal
->files
, file
);
433 SAFE_FREE(file
->fname
);
435 smbc_getFunctionRemoveUnusedServer(context
)(context
, srv
);
437 errno
= cli_status_to_errno(status
);
441 DLIST_REMOVE(context
->internal
->files
, file
);
442 SAFE_FREE(file
->fname
);
449 * Get info from an SMB server on a file. Use a qpathinfo call first
450 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
453 SMBC_getatr(SMBCCTX
* context
,
458 char *fixedpath
= NULL
;
459 char *targetpath
= NULL
;
460 struct cli_state
*targetcli
= NULL
;
463 struct timespec create_time_ts
= {0};
464 struct timespec access_time_ts
= {0};
465 struct timespec write_time_ts
= {0};
466 struct timespec change_time_ts
= {0};
467 struct timespec w_time_ts
= {0};
468 time_t write_time
= 0;
470 mode_t mode
= S_IFREG
;
471 struct cli_credentials
*creds
= NULL
;
472 TALLOC_CTX
*frame
= talloc_stackframe();
473 NTSTATUS status
= NT_STATUS_ACCESS_DENIED
;
475 if (!context
|| !context
->internal
->initialized
) {
477 return NT_STATUS_INVALID_PARAMETER
;
480 /* path fixup for . and .. */
481 if (ISDOT(path
) || ISDOTDOT(path
)) {
482 fixedpath
= talloc_strdup(frame
, "\\");
485 return NT_STATUS_NO_MEMORY
;
488 fixedpath
= talloc_strdup(frame
, path
);
491 return NT_STATUS_NO_MEMORY
;
493 trim_string(fixedpath
, NULL
, "\\..");
494 trim_string(fixedpath
, NULL
, "\\.");
496 DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
498 creds
= context
->internal
->creds
;
500 status
= cli_resolve_path(frame
, "",
503 &targetcli
, &targetpath
);
504 if (!NT_STATUS_IS_OK(status
)) {
505 d_printf("Couldn't resolve %s\n", path
);
507 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
510 if (!srv
->no_pathinfo2
) {
511 bool not_supported_error
= false;
512 status
= cli_qpathinfo2(targetcli
,
522 if (NT_STATUS_IS_OK(status
)) {
525 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_LEVEL
) ||
526 NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)) {
527 not_supported_error
= true;
529 if (!not_supported_error
) {
530 /* "Normal error". Just return it to caller. */
536 srv
->no_pathinfo2
= True
;
538 if (!srv
->no_pathinfo3
) {
539 bool not_supported_error
= false;
540 status
= cli_qpathinfo3(targetcli
,
549 if (NT_STATUS_IS_OK(status
)) {
552 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_LEVEL
) ||
553 NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)) {
554 not_supported_error
= true;
556 if (!not_supported_error
) {
557 /* "Normal error". Just return it to caller. */
563 srv
->no_pathinfo3
= True
;
565 /* if this is NT then don't bother with the getatr */
566 if (smb1cli_conn_capabilities(targetcli
->conn
) & CAP_NT_SMBS
) {
570 status
= cli_getatr(targetcli
, targetpath
, &attr
, &size
, &write_time
);
571 if (!NT_STATUS_IS_OK(status
)) {
574 w_time_ts
= convert_time_t_to_timespec(write_time
);
575 access_time_ts
= change_time_ts
= write_time_ts
= w_time_ts
;
588 if ((context
->internal
->posix_extensions
) && (mode
!= S_IFREG
)) {
589 sb
->st_mode
= (sb
->st_mode
& ~S_IFMT
) | mode
;
596 srv
->no_pathinfo2
= False
;
597 srv
->no_pathinfo3
= False
;
604 * Set file info on an SMB server. Use setpathinfo call first. If that
605 * fails, use setattrE..
607 * Access and modification time parameters are always used and must be
608 * provided. Create time, if zero, will be determined from the actual create
609 * time of the file. If non-zero, the create time will be set as well.
611 * "attr" (attributes) parameter may be set to -1 if it is not to be set.
614 SMBC_setatr(SMBCCTX
* context
, SMBCSRV
*srv
, char *path
,
615 struct timespec create_time
,
616 struct timespec access_time
,
617 struct timespec write_time
,
618 struct timespec change_time
,
622 uint32_t lattr
= (uint32_t)attr
;
624 TALLOC_CTX
*frame
= talloc_stackframe();
626 if (attr
== (uint16_t)-1) {
628 * External ABI only passes in
629 * 16-bits of attribute. Make
630 * sure we correctly map to
631 * (uint32_t)-1 meaning don't
632 * change attributes if attr was
633 * passed in as 16-bit -1.
635 lattr
= (uint32_t)-1;
640 * First, try setpathinfo (if qpathinfo succeeded), for it is the
641 * modern function for "new code" to be using, and it works given a
642 * filename rather than requiring that the file be opened to have its
643 * attributes manipulated.
645 if (srv
->no_pathinfo
||
646 !NT_STATUS_IS_OK(cli_setpathinfo_ext(srv
->cli
, path
,
654 * setpathinfo is not supported; go to plan B.
656 * cli_setatr() does not work on win98, and it also doesn't
657 * support setting the access time (only the modification
658 * time), so in all cases, we open the specified file and use
659 * cli_setattrE() which should work on all OS versions, and
660 * supports both times.
663 /* Don't try {q,set}pathinfo() again, with this server */
664 srv
->no_pathinfo
= True
;
667 status
= cli_open(srv
->cli
, path
, O_RDWR
, DENY_NONE
, &fd
);
668 if (!NT_STATUS_IS_OK(status
)) {
670 errno
= cli_status_to_errno(status
);
674 /* Set the new attributes */
675 status
= cli_setattrE(
683 cli_close(srv
->cli
, fd
);
686 * Unfortunately, setattrE() doesn't have a provision for
687 * setting the access attr (attributes). We'll have to try
688 * cli_setatr() for that, and with only this parameter, it
689 * seems to work on win98.
691 if (NT_STATUS_IS_OK(status
) && attr
!= (uint16_t) -1) {
692 status
= cli_setatr(srv
->cli
, path
, (uint32_t)attr
, 0);
695 if (!NT_STATUS_IS_OK(status
)) {
697 errno
= cli_status_to_errno(status
);
707 * A routine to lseek() a file
711 SMBC_lseek_ctx(SMBCCTX
*context
,
717 TALLOC_CTX
*frame
= talloc_stackframe();
719 if (!context
|| !context
->internal
->initialized
) {
725 if (!SMBC_dlist_contains(context
->internal
->files
, file
)) {
734 return -1; /* Can't lseek a dir ... */
739 file
->offset
= offset
;
742 file
->offset
+= offset
;
745 if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
746 file
->targetcli
, file
->cli_fd
, NULL
,
747 &size
, NULL
, NULL
, NULL
, NULL
,
753 file
->offset
= size
+ offset
;
766 * Routine to truncate a file given by its file descriptor, to a specified size
770 SMBC_ftruncate_ctx(SMBCCTX
*context
,
775 TALLOC_CTX
*frame
= talloc_stackframe();
777 if (!context
|| !context
->internal
->initialized
) {
783 if (!SMBC_dlist_contains(context
->internal
->files
, file
)) {
795 if (!NT_STATUS_IS_OK(cli_ftruncate(file
->targetcli
, file
->cli_fd
, (uint64_t)size
))) {