s3:torture:delete: simplify return code handling, fixing a couple of return codes...
[Samba.git] / source3 / libsmb / libsmb_file.c
blob9521542cb274a3c84fd4ff7d7b75dd450218f37a
1 /*
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/>.
25 #include "includes.h"
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 ...
35 SMBCFILE *
36 SMBC_open_ctx(SMBCCTX *context,
37 const char *fname,
38 int flags,
39 mode_t mode)
41 char *server = NULL;
42 char *share = NULL;
43 char *user = NULL;
44 char *password = NULL;
45 char *workgroup = NULL;
46 char *path = NULL;
47 char *targetpath = NULL;
48 struct cli_state *targetcli = NULL;
49 SMBCSRV *srv = NULL;
50 SMBCFILE *file = NULL;
51 uint16_t fd;
52 NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID;
53 TALLOC_CTX *frame = talloc_stackframe();
55 if (!context || !context->internal->initialized) {
56 errno = EINVAL; /* Best I can think of ... */
57 TALLOC_FREE(frame);
58 return NULL;
61 if (!fname) {
62 errno = EINVAL;
63 TALLOC_FREE(frame);
64 return NULL;
67 if (SMBC_parse_path(frame,
68 context,
69 fname,
70 &workgroup,
71 &server,
72 &share,
73 &path,
74 &user,
75 &password,
76 NULL)) {
77 errno = EINVAL;
78 TALLOC_FREE(frame);
79 return NULL;
82 if (!user || user[0] == (char)0) {
83 user = talloc_strdup(frame, smbc_getUser(context));
84 if (!user) {
85 errno = ENOMEM;
86 TALLOC_FREE(frame);
87 return NULL;
91 srv = SMBC_server(frame, context, True,
92 server, share, &workgroup, &user, &password);
93 if (!srv) {
94 if (errno == EPERM) errno = EACCES;
95 TALLOC_FREE(frame);
96 return NULL; /* SMBC_server sets errno */
99 /* Hmmm, the test for a directory is suspect here ... FIXME */
101 if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
102 status = NT_STATUS_OBJECT_PATH_INVALID;
103 } else {
104 file = SMB_MALLOC_P(SMBCFILE);
105 if (!file) {
106 errno = ENOMEM;
107 TALLOC_FREE(frame);
108 return NULL;
111 ZERO_STRUCTP(file);
113 /*d_printf(">>>open: resolving %s\n", path);*/
114 status = cli_resolve_path(
115 frame, "", context->internal->auth_info,
116 srv->cli, path, &targetcli, &targetpath);
117 if (!NT_STATUS_IS_OK(status)) {
118 d_printf("Could not resolve %s\n", path);
119 errno = ENOENT;
120 SAFE_FREE(file);
121 TALLOC_FREE(frame);
122 return NULL;
124 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
126 status = cli_open(targetcli, targetpath, flags,
127 context->internal->share_mode, &fd);
128 if (!NT_STATUS_IS_OK(status)) {
130 /* Handle the error ... */
132 SAFE_FREE(file);
133 errno = SMBC_errno(context, targetcli);
134 TALLOC_FREE(frame);
135 return NULL;
138 /* Fill in file struct */
140 file->cli_fd = fd;
141 file->fname = SMB_STRDUP(fname);
142 file->srv = srv;
143 file->offset = 0;
144 file->file = True;
146 DLIST_ADD(context->internal->files, file);
149 * If the file was opened in O_APPEND mode, all write
150 * operations should be appended to the file. To do that,
151 * though, using this protocol, would require a getattrE()
152 * call for each and every write, to determine where the end
153 * of the file is. (There does not appear to be an append flag
154 * in the protocol.) Rather than add all of that overhead of
155 * retrieving the current end-of-file offset prior to each
156 * write operation, we'll assume that most append operations
157 * will continuously write, so we'll just set the offset to
158 * the end of the file now and hope that's adequate.
160 * Note to self: If this proves inadequate, and O_APPEND
161 * should, in some cases, be forced for each write, add a
162 * field in the context options structure, for
163 * "strict_append_mode" which would select between the current
164 * behavior (if FALSE) or issuing a getattrE() prior to each
165 * write and forcing the write to the end of the file (if
166 * TRUE). Adding that capability will likely require adding
167 * an "append" flag into the _SMBCFILE structure to track
168 * whether a file was opened in O_APPEND mode. -- djl
170 if (flags & O_APPEND) {
171 if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
172 (void) SMBC_close_ctx(context, file);
173 errno = ENXIO;
174 TALLOC_FREE(frame);
175 return NULL;
179 TALLOC_FREE(frame);
180 return file;
183 /* Check if opendir needed ... */
185 if (!NT_STATUS_IS_OK(status)) {
186 int eno = 0;
188 eno = SMBC_errno(context, srv->cli);
189 file = smbc_getFunctionOpendir(context)(context, fname);
190 if (!file) errno = eno;
191 TALLOC_FREE(frame);
192 return file;
195 errno = EINVAL; /* FIXME, correct errno ? */
196 TALLOC_FREE(frame);
197 return NULL;
201 * Routine to create a file
204 SMBCFILE *
205 SMBC_creat_ctx(SMBCCTX *context,
206 const char *path,
207 mode_t mode)
209 if (!context || !context->internal->initialized) {
210 errno = EINVAL;
211 return NULL;
214 return SMBC_open_ctx(context, path,
215 O_WRONLY | O_CREAT | O_TRUNC, mode);
219 * Routine to read() a file ...
222 ssize_t
223 SMBC_read_ctx(SMBCCTX *context,
224 SMBCFILE *file,
225 void *buf,
226 size_t count)
228 size_t ret;
229 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
230 char *path = NULL;
231 char *targetpath = NULL;
232 struct cli_state *targetcli = NULL;
233 TALLOC_CTX *frame = talloc_stackframe();
234 NTSTATUS status;
237 * offset:
239 * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
240 * appears to pass file->offset (which is type off_t) differently than
241 * a local variable of type off_t. Using local variable "offset" in
242 * the call to cli_read() instead of file->offset fixes a problem
243 * retrieving data at an offset greater than 4GB.
245 off_t offset;
247 if (!context || !context->internal->initialized) {
248 errno = EINVAL;
249 TALLOC_FREE(frame);
250 return -1;
253 DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
255 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
256 errno = EBADF;
257 TALLOC_FREE(frame);
258 return -1;
261 offset = file->offset;
263 /* Check that the buffer exists ... */
265 if (buf == NULL) {
266 errno = EINVAL;
267 TALLOC_FREE(frame);
268 return -1;
271 /*d_printf(">>>read: parsing %s\n", file->fname);*/
272 if (SMBC_parse_path(frame,
273 context,
274 file->fname,
275 NULL,
276 &server,
277 &share,
278 &path,
279 &user,
280 &password,
281 NULL)) {
282 errno = EINVAL;
283 TALLOC_FREE(frame);
284 return -1;
287 /*d_printf(">>>read: resolving %s\n", path);*/
288 status = cli_resolve_path(frame, "", context->internal->auth_info,
289 file->srv->cli, path,
290 &targetcli, &targetpath);
291 if (!NT_STATUS_IS_OK(status)) {
292 d_printf("Could not resolve %s\n", path);
293 errno = ENOENT;
294 TALLOC_FREE(frame);
295 return -1;
297 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
299 status = cli_read(targetcli, file->cli_fd, (char *)buf, offset,
300 count, &ret);
301 if (!NT_STATUS_IS_OK(status)) {
302 errno = SMBC_errno(context, targetcli);
303 TALLOC_FREE(frame);
304 return -1;
307 file->offset += ret;
309 DEBUG(4, (" --> %ld\n", (unsigned long)ret));
311 TALLOC_FREE(frame);
312 return ret; /* Success, ret bytes of data ... */
316 * Routine to write() a file ...
319 ssize_t
320 SMBC_write_ctx(SMBCCTX *context,
321 SMBCFILE *file,
322 const void *buf,
323 size_t count)
325 off_t offset;
326 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
327 char *path = NULL;
328 char *targetpath = NULL;
329 struct cli_state *targetcli = NULL;
330 TALLOC_CTX *frame = talloc_stackframe();
331 NTSTATUS status;
333 /* First check all pointers before dereferencing them */
335 if (!context || !context->internal->initialized) {
336 errno = EINVAL;
337 TALLOC_FREE(frame);
338 return -1;
341 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
342 errno = EBADF;
343 TALLOC_FREE(frame);
344 return -1;
347 /* Check that the buffer exists ... */
349 if (buf == NULL) {
350 errno = EINVAL;
351 TALLOC_FREE(frame);
352 return -1;
355 offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
357 /*d_printf(">>>write: parsing %s\n", file->fname);*/
358 if (SMBC_parse_path(frame,
359 context,
360 file->fname,
361 NULL,
362 &server,
363 &share,
364 &path,
365 &user,
366 &password,
367 NULL)) {
368 errno = EINVAL;
369 TALLOC_FREE(frame);
370 return -1;
373 /*d_printf(">>>write: resolving %s\n", path);*/
374 status = cli_resolve_path(frame, "", context->internal->auth_info,
375 file->srv->cli, path,
376 &targetcli, &targetpath);
377 if (!NT_STATUS_IS_OK(status)) {
378 d_printf("Could not resolve %s\n", path);
379 errno = ENOENT;
380 TALLOC_FREE(frame);
381 return -1;
383 /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
385 status = cli_writeall(targetcli, file->cli_fd,
386 0, (const uint8_t *)buf, offset, count, NULL);
387 if (!NT_STATUS_IS_OK(status)) {
388 errno = map_errno_from_nt_status(status);
389 TALLOC_FREE(frame);
390 return -1;
393 file->offset += count;
395 TALLOC_FREE(frame);
396 return count; /* Success, 0 bytes of data ... */
400 * Routine to close() a file ...
404 SMBC_close_ctx(SMBCCTX *context,
405 SMBCFILE *file)
407 SMBCSRV *srv;
408 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
409 char *path = NULL;
410 char *targetpath = NULL;
411 struct cli_state *targetcli = NULL;
412 TALLOC_CTX *frame = talloc_stackframe();
413 NTSTATUS status;
415 if (!context || !context->internal->initialized) {
416 errno = EINVAL;
417 TALLOC_FREE(frame);
418 return -1;
421 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
422 errno = EBADF;
423 TALLOC_FREE(frame);
424 return -1;
427 /* IS a dir ... */
428 if (!file->file) {
429 TALLOC_FREE(frame);
430 return smbc_getFunctionClosedir(context)(context, file);
433 /*d_printf(">>>close: parsing %s\n", file->fname);*/
434 if (SMBC_parse_path(frame,
435 context,
436 file->fname,
437 NULL,
438 &server,
439 &share,
440 &path,
441 &user,
442 &password,
443 NULL)) {
444 errno = EINVAL;
445 TALLOC_FREE(frame);
446 return -1;
449 /*d_printf(">>>close: resolving %s\n", path);*/
450 status = cli_resolve_path(frame, "", context->internal->auth_info,
451 file->srv->cli, path,
452 &targetcli, &targetpath);
453 if (!NT_STATUS_IS_OK(status)) {
454 d_printf("Could not resolve %s\n", path);
455 errno = ENOENT;
456 TALLOC_FREE(frame);
457 return -1;
459 /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
461 if (!NT_STATUS_IS_OK(cli_close(targetcli, file->cli_fd))) {
462 DEBUG(3, ("cli_close failed on %s. purging server.\n",
463 file->fname));
464 /* Deallocate slot and remove the server
465 * from the server cache if unused */
466 errno = SMBC_errno(context, targetcli);
467 srv = file->srv;
468 DLIST_REMOVE(context->internal->files, file);
469 SAFE_FREE(file->fname);
470 SAFE_FREE(file);
471 smbc_getFunctionRemoveUnusedServer(context)(context, srv);
472 TALLOC_FREE(frame);
473 return -1;
476 DLIST_REMOVE(context->internal->files, file);
477 SAFE_FREE(file->fname);
478 SAFE_FREE(file);
479 TALLOC_FREE(frame);
480 return 0;
484 * Get info from an SMB server on a file. Use a qpathinfo call first
485 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
487 bool
488 SMBC_getatr(SMBCCTX * context,
489 SMBCSRV *srv,
490 const char *path,
491 uint16 *mode,
492 off_t *size,
493 struct timespec *create_time_ts,
494 struct timespec *access_time_ts,
495 struct timespec *write_time_ts,
496 struct timespec *change_time_ts,
497 SMB_INO_T *ino)
499 char *fixedpath = NULL;
500 char *targetpath = NULL;
501 struct cli_state *targetcli = NULL;
502 time_t write_time;
503 TALLOC_CTX *frame = talloc_stackframe();
504 NTSTATUS status;
506 if (!context || !context->internal->initialized) {
507 errno = EINVAL;
508 TALLOC_FREE(frame);
509 return False;
512 /* path fixup for . and .. */
513 if (strequal(path, ".") || strequal(path, "..")) {
514 fixedpath = talloc_strdup(frame, "\\");
515 if (!fixedpath) {
516 errno = ENOMEM;
517 TALLOC_FREE(frame);
518 return False;
520 } else {
521 fixedpath = talloc_strdup(frame, path);
522 if (!fixedpath) {
523 errno = ENOMEM;
524 TALLOC_FREE(frame);
525 return False;
527 trim_string(fixedpath, NULL, "\\..");
528 trim_string(fixedpath, NULL, "\\.");
530 DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
532 status = cli_resolve_path(frame, "", context->internal->auth_info,
533 srv->cli, fixedpath,
534 &targetcli, &targetpath);
535 if (!NT_STATUS_IS_OK(status)) {
536 d_printf("Couldn't resolve %s\n", path);
537 errno = ENOENT;
538 TALLOC_FREE(frame);
539 return False;
542 if (!srv->no_pathinfo2 &&
543 NT_STATUS_IS_OK(cli_qpathinfo2(targetcli, targetpath,
544 create_time_ts,
545 access_time_ts,
546 write_time_ts,
547 change_time_ts,
548 size, mode, ino))) {
549 TALLOC_FREE(frame);
550 return True;
553 /* if this is NT then don't bother with the getatr */
554 if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
555 errno = EPERM;
556 TALLOC_FREE(frame);
557 return False;
560 if (NT_STATUS_IS_OK(cli_getatr(targetcli, targetpath, mode, size, &write_time))) {
561 struct timespec w_time_ts;
563 w_time_ts = convert_time_t_to_timespec(write_time);
564 if (write_time_ts != NULL) {
565 *write_time_ts = w_time_ts;
567 if (create_time_ts != NULL) {
568 *create_time_ts = w_time_ts;
570 if (access_time_ts != NULL) {
571 *access_time_ts = w_time_ts;
573 if (change_time_ts != NULL) {
574 *change_time_ts = w_time_ts;
576 srv->no_pathinfo2 = True;
577 TALLOC_FREE(frame);
578 return True;
581 errno = EPERM;
582 TALLOC_FREE(frame);
583 return False;
587 * Set file info on an SMB server. Use setpathinfo call first. If that
588 * fails, use setattrE..
590 * Access and modification time parameters are always used and must be
591 * provided. Create time, if zero, will be determined from the actual create
592 * time of the file. If non-zero, the create time will be set as well.
594 * "mode" (attributes) parameter may be set to -1 if it is not to be set.
596 bool
597 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
598 time_t create_time,
599 time_t access_time,
600 time_t write_time,
601 time_t change_time,
602 uint16 mode)
604 uint16_t fd;
605 int ret;
606 TALLOC_CTX *frame = talloc_stackframe();
609 * First, try setpathinfo (if qpathinfo succeeded), for it is the
610 * modern function for "new code" to be using, and it works given a
611 * filename rather than requiring that the file be opened to have its
612 * attributes manipulated.
614 if (srv->no_pathinfo ||
615 !NT_STATUS_IS_OK(cli_setpathinfo_basic(srv->cli, path,
616 create_time,
617 access_time,
618 write_time,
619 change_time,
620 mode))) {
623 * setpathinfo is not supported; go to plan B.
625 * cli_setatr() does not work on win98, and it also doesn't
626 * support setting the access time (only the modification
627 * time), so in all cases, we open the specified file and use
628 * cli_setattrE() which should work on all OS versions, and
629 * supports both times.
632 /* Don't try {q,set}pathinfo() again, with this server */
633 srv->no_pathinfo = True;
635 /* Open the file */
636 if (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) {
637 errno = SMBC_errno(context, srv->cli);
638 TALLOC_FREE(frame);
639 return -1;
642 /* Set the new attributes */
643 ret = NT_STATUS_IS_OK(cli_setattrE(srv->cli, fd,
644 change_time,
645 access_time,
646 write_time));
648 /* Close the file */
649 cli_close(srv->cli, fd);
652 * Unfortunately, setattrE() doesn't have a provision for
653 * setting the access mode (attributes). We'll have to try
654 * cli_setatr() for that, and with only this parameter, it
655 * seems to work on win98.
657 if (ret && mode != (uint16) -1) {
658 ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0));
661 if (! ret) {
662 errno = SMBC_errno(context, srv->cli);
663 TALLOC_FREE(frame);
664 return False;
668 TALLOC_FREE(frame);
669 return True;
673 * A routine to lseek() a file
676 off_t
677 SMBC_lseek_ctx(SMBCCTX *context,
678 SMBCFILE *file,
679 off_t offset,
680 int whence)
682 off_t size;
683 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
684 char *path = NULL;
685 char *targetpath = NULL;
686 struct cli_state *targetcli = NULL;
687 TALLOC_CTX *frame = talloc_stackframe();
688 NTSTATUS status;
690 if (!context || !context->internal->initialized) {
691 errno = EINVAL;
692 TALLOC_FREE(frame);
693 return -1;
696 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
697 errno = EBADF;
698 TALLOC_FREE(frame);
699 return -1;
702 if (!file->file) {
703 errno = EINVAL;
704 TALLOC_FREE(frame);
705 return -1; /* Can't lseek a dir ... */
708 switch (whence) {
709 case SEEK_SET:
710 file->offset = offset;
711 break;
712 case SEEK_CUR:
713 file->offset += offset;
714 break;
715 case SEEK_END:
716 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
717 if (SMBC_parse_path(frame,
718 context,
719 file->fname,
720 NULL,
721 &server,
722 &share,
723 &path,
724 &user,
725 &password,
726 NULL)) {
727 errno = EINVAL;
728 TALLOC_FREE(frame);
729 return -1;
732 /*d_printf(">>>lseek: resolving %s\n", path);*/
733 status = cli_resolve_path(
734 frame, "", context->internal->auth_info,
735 file->srv->cli, path, &targetcli, &targetpath);
736 if (!NT_STATUS_IS_OK(status)) {
737 d_printf("Could not resolve %s\n", path);
738 errno = ENOENT;
739 TALLOC_FREE(frame);
740 return -1;
743 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
744 if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
745 targetcli, file->cli_fd, NULL,
746 &size, NULL, NULL, NULL, NULL,
747 NULL))) {
748 off_t b_size = size;
749 if (!NT_STATUS_IS_OK(cli_getattrE(targetcli, file->cli_fd,
750 NULL, &b_size, NULL, NULL, NULL))) {
751 errno = EINVAL;
752 TALLOC_FREE(frame);
753 return -1;
754 } else
755 size = b_size;
757 file->offset = size + offset;
758 break;
759 default:
760 errno = EINVAL;
761 break;
764 TALLOC_FREE(frame);
765 return file->offset;
770 * Routine to truncate a file given by its file descriptor, to a specified size
774 SMBC_ftruncate_ctx(SMBCCTX *context,
775 SMBCFILE *file,
776 off_t length)
778 off_t size = length;
779 char *server = NULL;
780 char *share = NULL;
781 char *user = NULL;
782 char *password = NULL;
783 char *path = NULL;
784 char *targetpath = NULL;
785 struct cli_state *targetcli = NULL;
786 TALLOC_CTX *frame = talloc_stackframe();
787 NTSTATUS status;
789 if (!context || !context->internal->initialized) {
790 errno = EINVAL;
791 TALLOC_FREE(frame);
792 return -1;
795 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
796 errno = EBADF;
797 TALLOC_FREE(frame);
798 return -1;
801 if (!file->file) {
802 errno = EINVAL;
803 TALLOC_FREE(frame);
804 return -1;
807 /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
808 if (SMBC_parse_path(frame,
809 context,
810 file->fname,
811 NULL,
812 &server,
813 &share,
814 &path,
815 &user,
816 &password,
817 NULL)) {
818 errno = EINVAL;
819 TALLOC_FREE(frame);
820 return -1;
823 /*d_printf(">>>fstat: resolving %s\n", path);*/
824 status = cli_resolve_path(frame, "", context->internal->auth_info,
825 file->srv->cli, path,
826 &targetcli, &targetpath);
827 if (!NT_STATUS_IS_OK(status)) {
828 d_printf("Could not resolve %s\n", path);
829 errno = ENOENT;
830 TALLOC_FREE(frame);
831 return -1;
833 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
835 if (!NT_STATUS_IS_OK(cli_ftruncate(targetcli, file->cli_fd, (uint64_t)size))) {
836 errno = EINVAL;
837 TALLOC_FREE(frame);
838 return -1;
841 TALLOC_FREE(frame);
842 return 0;