krb5wrap: Move mask to the right position.
[Samba/id10ts.git] / source3 / libsmb / libsmb_file.c
blob822491bda88295bb9a08e79ad56d894284e53199
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 uint16_t port = 0;
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 ... */
58 TALLOC_FREE(frame);
59 return NULL;
62 if (!fname) {
63 errno = EINVAL;
64 TALLOC_FREE(frame);
65 return NULL;
68 if (SMBC_parse_path(frame,
69 context,
70 fname,
71 &workgroup,
72 &server,
73 &port,
74 &share,
75 &path,
76 &user,
77 &password,
78 NULL)) {
79 errno = EINVAL;
80 TALLOC_FREE(frame);
81 return NULL;
84 if (!user || user[0] == (char)0) {
85 user = talloc_strdup(frame, smbc_getUser(context));
86 if (!user) {
87 errno = ENOMEM;
88 TALLOC_FREE(frame);
89 return NULL;
93 srv = SMBC_server(frame, context, True,
94 server, port, share, &workgroup, &user, &password);
95 if (!srv) {
96 if (errno == EPERM) errno = EACCES;
97 TALLOC_FREE(frame);
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;
105 } else {
106 file = SMB_MALLOC_P(SMBCFILE);
107 if (!file) {
108 errno = ENOMEM;
109 TALLOC_FREE(frame);
110 return NULL;
113 ZERO_STRUCTP(file);
115 /*d_printf(">>>open: resolving %s\n", path);*/
116 status = cli_resolve_path(
117 frame, "", context->internal->auth_info,
118 srv->cli, path, &targetcli, &targetpath);
119 if (!NT_STATUS_IS_OK(status)) {
120 d_printf("Could not resolve %s\n", path);
121 errno = ENOENT;
122 SAFE_FREE(file);
123 TALLOC_FREE(frame);
124 return NULL;
126 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
128 status = cli_open(targetcli, targetpath, flags,
129 context->internal->share_mode, &fd);
130 if (!NT_STATUS_IS_OK(status)) {
132 /* Handle the error ... */
134 SAFE_FREE(file);
135 errno = SMBC_errno(context, targetcli);
136 TALLOC_FREE(frame);
137 return NULL;
140 /* Fill in file struct */
142 file->cli_fd = fd;
143 file->fname = SMB_STRDUP(fname);
144 file->srv = srv;
145 file->offset = 0;
146 file->file = True;
148 DLIST_ADD(context->internal->files, file);
151 * If the file was opened in O_APPEND mode, all write
152 * operations should be appended to the file. To do that,
153 * though, using this protocol, would require a getattrE()
154 * call for each and every write, to determine where the end
155 * of the file is. (There does not appear to be an append flag
156 * in the protocol.) Rather than add all of that overhead of
157 * retrieving the current end-of-file offset prior to each
158 * write operation, we'll assume that most append operations
159 * will continuously write, so we'll just set the offset to
160 * the end of the file now and hope that's adequate.
162 * Note to self: If this proves inadequate, and O_APPEND
163 * should, in some cases, be forced for each write, add a
164 * field in the context options structure, for
165 * "strict_append_mode" which would select between the current
166 * behavior (if FALSE) or issuing a getattrE() prior to each
167 * write and forcing the write to the end of the file (if
168 * TRUE). Adding that capability will likely require adding
169 * an "append" flag into the _SMBCFILE structure to track
170 * whether a file was opened in O_APPEND mode. -- djl
172 if (flags & O_APPEND) {
173 if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
174 (void) SMBC_close_ctx(context, file);
175 errno = ENXIO;
176 TALLOC_FREE(frame);
177 return NULL;
181 TALLOC_FREE(frame);
182 return file;
185 /* Check if opendir needed ... */
187 if (!NT_STATUS_IS_OK(status)) {
188 int eno = 0;
190 eno = SMBC_errno(context, srv->cli);
191 file = smbc_getFunctionOpendir(context)(context, fname);
192 if (!file) errno = eno;
193 TALLOC_FREE(frame);
194 return file;
197 errno = EINVAL; /* FIXME, correct errno ? */
198 TALLOC_FREE(frame);
199 return NULL;
203 * Routine to create a file
206 SMBCFILE *
207 SMBC_creat_ctx(SMBCCTX *context,
208 const char *path,
209 mode_t mode)
211 if (!context || !context->internal->initialized) {
212 errno = EINVAL;
213 return NULL;
216 return SMBC_open_ctx(context, path,
217 O_WRONLY | O_CREAT | O_TRUNC, mode);
221 * Routine to read() a file ...
224 ssize_t
225 SMBC_read_ctx(SMBCCTX *context,
226 SMBCFILE *file,
227 void *buf,
228 size_t count)
230 size_t ret;
231 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
232 char *path = NULL;
233 char *targetpath = NULL;
234 struct cli_state *targetcli = NULL;
235 uint16_t port = 0;
236 TALLOC_CTX *frame = talloc_stackframe();
237 NTSTATUS status;
240 * offset:
242 * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
243 * appears to pass file->offset (which is type off_t) differently than
244 * a local variable of type off_t. Using local variable "offset" in
245 * the call to cli_read() instead of file->offset fixes a problem
246 * retrieving data at an offset greater than 4GB.
248 off_t offset;
250 if (!context || !context->internal->initialized) {
251 errno = EINVAL;
252 TALLOC_FREE(frame);
253 return -1;
256 DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
258 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
259 errno = EBADF;
260 TALLOC_FREE(frame);
261 return -1;
264 offset = file->offset;
266 /* Check that the buffer exists ... */
268 if (buf == NULL) {
269 errno = EINVAL;
270 TALLOC_FREE(frame);
271 return -1;
274 /*d_printf(">>>read: parsing %s\n", file->fname);*/
275 if (SMBC_parse_path(frame,
276 context,
277 file->fname,
278 NULL,
279 &server,
280 &port,
281 &share,
282 &path,
283 &user,
284 &password,
285 NULL)) {
286 errno = EINVAL;
287 TALLOC_FREE(frame);
288 return -1;
291 /*d_printf(">>>read: resolving %s\n", path);*/
292 status = cli_resolve_path(frame, "", context->internal->auth_info,
293 file->srv->cli, path,
294 &targetcli, &targetpath);
295 if (!NT_STATUS_IS_OK(status)) {
296 d_printf("Could not resolve %s\n", path);
297 errno = ENOENT;
298 TALLOC_FREE(frame);
299 return -1;
301 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
303 status = cli_read(targetcli, file->cli_fd, (char *)buf, offset,
304 count, &ret);
305 if (!NT_STATUS_IS_OK(status)) {
306 errno = SMBC_errno(context, targetcli);
307 TALLOC_FREE(frame);
308 return -1;
311 file->offset += ret;
313 DEBUG(4, (" --> %ld\n", (unsigned long)ret));
315 TALLOC_FREE(frame);
316 return ret; /* Success, ret bytes of data ... */
320 * Routine to write() a file ...
323 ssize_t
324 SMBC_write_ctx(SMBCCTX *context,
325 SMBCFILE *file,
326 const void *buf,
327 size_t count)
329 off_t offset;
330 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
331 char *path = NULL;
332 char *targetpath = NULL;
333 struct cli_state *targetcli = NULL;
334 uint16_t port = 0;
335 TALLOC_CTX *frame = talloc_stackframe();
336 NTSTATUS status;
338 /* First check all pointers before dereferencing them */
340 if (!context || !context->internal->initialized) {
341 errno = EINVAL;
342 TALLOC_FREE(frame);
343 return -1;
346 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
347 errno = EBADF;
348 TALLOC_FREE(frame);
349 return -1;
352 /* Check that the buffer exists ... */
354 if (buf == NULL) {
355 errno = EINVAL;
356 TALLOC_FREE(frame);
357 return -1;
360 offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
362 /*d_printf(">>>write: parsing %s\n", file->fname);*/
363 if (SMBC_parse_path(frame,
364 context,
365 file->fname,
366 NULL,
367 &server,
368 &port,
369 &share,
370 &path,
371 &user,
372 &password,
373 NULL)) {
374 errno = EINVAL;
375 TALLOC_FREE(frame);
376 return -1;
379 /*d_printf(">>>write: resolving %s\n", path);*/
380 status = cli_resolve_path(frame, "", context->internal->auth_info,
381 file->srv->cli, path,
382 &targetcli, &targetpath);
383 if (!NT_STATUS_IS_OK(status)) {
384 d_printf("Could not resolve %s\n", path);
385 errno = ENOENT;
386 TALLOC_FREE(frame);
387 return -1;
389 /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
391 status = cli_writeall(targetcli, file->cli_fd,
392 0, (const uint8_t *)buf, offset, count, NULL);
393 if (!NT_STATUS_IS_OK(status)) {
394 errno = map_errno_from_nt_status(status);
395 TALLOC_FREE(frame);
396 return -1;
399 file->offset += count;
401 TALLOC_FREE(frame);
402 return count; /* Success, 0 bytes of data ... */
406 * Routine to close() a file ...
410 SMBC_close_ctx(SMBCCTX *context,
411 SMBCFILE *file)
413 SMBCSRV *srv;
414 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
415 char *path = NULL;
416 char *targetpath = NULL;
417 uint16_t port = 0;
418 struct cli_state *targetcli = NULL;
419 TALLOC_CTX *frame = talloc_stackframe();
420 NTSTATUS status;
422 if (!context || !context->internal->initialized) {
423 errno = EINVAL;
424 TALLOC_FREE(frame);
425 return -1;
428 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
429 errno = EBADF;
430 TALLOC_FREE(frame);
431 return -1;
434 /* IS a dir ... */
435 if (!file->file) {
436 TALLOC_FREE(frame);
437 return smbc_getFunctionClosedir(context)(context, file);
440 /*d_printf(">>>close: parsing %s\n", file->fname);*/
441 if (SMBC_parse_path(frame,
442 context,
443 file->fname,
444 NULL,
445 &server,
446 &port,
447 &share,
448 &path,
449 &user,
450 &password,
451 NULL)) {
452 errno = EINVAL;
453 TALLOC_FREE(frame);
454 return -1;
457 /*d_printf(">>>close: resolving %s\n", path);*/
458 status = cli_resolve_path(frame, "", context->internal->auth_info,
459 file->srv->cli, path,
460 &targetcli, &targetpath);
461 if (!NT_STATUS_IS_OK(status)) {
462 d_printf("Could not resolve %s\n", path);
463 errno = ENOENT;
464 TALLOC_FREE(frame);
465 return -1;
467 /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
469 if (!NT_STATUS_IS_OK(cli_close(targetcli, file->cli_fd))) {
470 DEBUG(3, ("cli_close failed on %s. purging server.\n",
471 file->fname));
472 /* Deallocate slot and remove the server
473 * from the server cache if unused */
474 errno = SMBC_errno(context, targetcli);
475 srv = file->srv;
476 DLIST_REMOVE(context->internal->files, file);
477 SAFE_FREE(file->fname);
478 SAFE_FREE(file);
479 smbc_getFunctionRemoveUnusedServer(context)(context, srv);
480 TALLOC_FREE(frame);
481 return -1;
484 DLIST_REMOVE(context->internal->files, file);
485 SAFE_FREE(file->fname);
486 SAFE_FREE(file);
487 TALLOC_FREE(frame);
488 return 0;
492 * Get info from an SMB server on a file. Use a qpathinfo call first
493 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
495 bool
496 SMBC_getatr(SMBCCTX * context,
497 SMBCSRV *srv,
498 const char *path,
499 uint16 *mode,
500 off_t *size,
501 struct timespec *create_time_ts,
502 struct timespec *access_time_ts,
503 struct timespec *write_time_ts,
504 struct timespec *change_time_ts,
505 SMB_INO_T *ino)
507 char *fixedpath = NULL;
508 char *targetpath = NULL;
509 struct cli_state *targetcli = NULL;
510 time_t write_time;
511 TALLOC_CTX *frame = talloc_stackframe();
512 NTSTATUS status;
514 if (!context || !context->internal->initialized) {
515 errno = EINVAL;
516 TALLOC_FREE(frame);
517 return False;
520 /* path fixup for . and .. */
521 if (strequal(path, ".") || strequal(path, "..")) {
522 fixedpath = talloc_strdup(frame, "\\");
523 if (!fixedpath) {
524 errno = ENOMEM;
525 TALLOC_FREE(frame);
526 return False;
528 } else {
529 fixedpath = talloc_strdup(frame, path);
530 if (!fixedpath) {
531 errno = ENOMEM;
532 TALLOC_FREE(frame);
533 return False;
535 trim_string(fixedpath, NULL, "\\..");
536 trim_string(fixedpath, NULL, "\\.");
538 DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
540 status = cli_resolve_path(frame, "", context->internal->auth_info,
541 srv->cli, fixedpath,
542 &targetcli, &targetpath);
543 if (!NT_STATUS_IS_OK(status)) {
544 d_printf("Couldn't resolve %s\n", path);
545 errno = ENOENT;
546 TALLOC_FREE(frame);
547 return False;
550 if (!srv->no_pathinfo2 &&
551 NT_STATUS_IS_OK(cli_qpathinfo2(targetcli, targetpath,
552 create_time_ts,
553 access_time_ts,
554 write_time_ts,
555 change_time_ts,
556 size, mode, ino))) {
557 TALLOC_FREE(frame);
558 return True;
561 /* if this is NT then don't bother with the getatr */
562 if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
563 errno = EPERM;
564 TALLOC_FREE(frame);
565 return False;
568 if (NT_STATUS_IS_OK(cli_getatr(targetcli, targetpath, mode, size, &write_time))) {
569 struct timespec w_time_ts;
571 w_time_ts = convert_time_t_to_timespec(write_time);
572 if (write_time_ts != NULL) {
573 *write_time_ts = w_time_ts;
575 if (create_time_ts != NULL) {
576 *create_time_ts = w_time_ts;
578 if (access_time_ts != NULL) {
579 *access_time_ts = w_time_ts;
581 if (change_time_ts != NULL) {
582 *change_time_ts = w_time_ts;
584 srv->no_pathinfo2 = True;
585 TALLOC_FREE(frame);
586 return True;
589 errno = EPERM;
590 TALLOC_FREE(frame);
591 return False;
595 * Set file info on an SMB server. Use setpathinfo call first. If that
596 * fails, use setattrE..
598 * Access and modification time parameters are always used and must be
599 * provided. Create time, if zero, will be determined from the actual create
600 * time of the file. If non-zero, the create time will be set as well.
602 * "mode" (attributes) parameter may be set to -1 if it is not to be set.
604 bool
605 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
606 time_t create_time,
607 time_t access_time,
608 time_t write_time,
609 time_t change_time,
610 uint16 mode)
612 uint16_t fd;
613 int ret;
614 TALLOC_CTX *frame = talloc_stackframe();
617 * First, try setpathinfo (if qpathinfo succeeded), for it is the
618 * modern function for "new code" to be using, and it works given a
619 * filename rather than requiring that the file be opened to have its
620 * attributes manipulated.
622 if (srv->no_pathinfo ||
623 !NT_STATUS_IS_OK(cli_setpathinfo_basic(srv->cli, path,
624 create_time,
625 access_time,
626 write_time,
627 change_time,
628 mode))) {
631 * setpathinfo is not supported; go to plan B.
633 * cli_setatr() does not work on win98, and it also doesn't
634 * support setting the access time (only the modification
635 * time), so in all cases, we open the specified file and use
636 * cli_setattrE() which should work on all OS versions, and
637 * supports both times.
640 /* Don't try {q,set}pathinfo() again, with this server */
641 srv->no_pathinfo = True;
643 /* Open the file */
644 if (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) {
645 errno = SMBC_errno(context, srv->cli);
646 TALLOC_FREE(frame);
647 return -1;
650 /* Set the new attributes */
651 ret = NT_STATUS_IS_OK(cli_setattrE(srv->cli, fd,
652 change_time,
653 access_time,
654 write_time));
656 /* Close the file */
657 cli_close(srv->cli, fd);
660 * Unfortunately, setattrE() doesn't have a provision for
661 * setting the access mode (attributes). We'll have to try
662 * cli_setatr() for that, and with only this parameter, it
663 * seems to work on win98.
665 if (ret && mode != (uint16) -1) {
666 ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0));
669 if (! ret) {
670 errno = SMBC_errno(context, srv->cli);
671 TALLOC_FREE(frame);
672 return False;
676 TALLOC_FREE(frame);
677 return True;
681 * A routine to lseek() a file
684 off_t
685 SMBC_lseek_ctx(SMBCCTX *context,
686 SMBCFILE *file,
687 off_t offset,
688 int whence)
690 off_t size;
691 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
692 char *path = NULL;
693 char *targetpath = NULL;
694 struct cli_state *targetcli = NULL;
695 uint16_t port = 0;
696 TALLOC_CTX *frame = talloc_stackframe();
697 NTSTATUS status;
699 if (!context || !context->internal->initialized) {
700 errno = EINVAL;
701 TALLOC_FREE(frame);
702 return -1;
705 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
706 errno = EBADF;
707 TALLOC_FREE(frame);
708 return -1;
711 if (!file->file) {
712 errno = EINVAL;
713 TALLOC_FREE(frame);
714 return -1; /* Can't lseek a dir ... */
717 switch (whence) {
718 case SEEK_SET:
719 file->offset = offset;
720 break;
721 case SEEK_CUR:
722 file->offset += offset;
723 break;
724 case SEEK_END:
725 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
726 if (SMBC_parse_path(frame,
727 context,
728 file->fname,
729 NULL,
730 &server,
731 &port,
732 &share,
733 &path,
734 &user,
735 &password,
736 NULL)) {
737 errno = EINVAL;
738 TALLOC_FREE(frame);
739 return -1;
742 /*d_printf(">>>lseek: resolving %s\n", path);*/
743 status = cli_resolve_path(
744 frame, "", context->internal->auth_info,
745 file->srv->cli, path, &targetcli, &targetpath);
746 if (!NT_STATUS_IS_OK(status)) {
747 d_printf("Could not resolve %s\n", path);
748 errno = ENOENT;
749 TALLOC_FREE(frame);
750 return -1;
753 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
754 if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
755 targetcli, file->cli_fd, NULL,
756 &size, NULL, NULL, NULL, NULL,
757 NULL))) {
758 off_t b_size = size;
759 if (!NT_STATUS_IS_OK(cli_getattrE(targetcli, file->cli_fd,
760 NULL, &b_size, NULL, NULL, NULL))) {
761 errno = EINVAL;
762 TALLOC_FREE(frame);
763 return -1;
764 } else
765 size = b_size;
767 file->offset = size + offset;
768 break;
769 default:
770 errno = EINVAL;
771 break;
774 TALLOC_FREE(frame);
775 return file->offset;
780 * Routine to truncate a file given by its file descriptor, to a specified size
784 SMBC_ftruncate_ctx(SMBCCTX *context,
785 SMBCFILE *file,
786 off_t length)
788 off_t size = length;
789 char *server = NULL;
790 char *share = NULL;
791 char *user = NULL;
792 char *password = NULL;
793 char *path = NULL;
794 char *targetpath = NULL;
795 uint16_t port = 0;
796 struct cli_state *targetcli = NULL;
797 TALLOC_CTX *frame = talloc_stackframe();
798 NTSTATUS status;
800 if (!context || !context->internal->initialized) {
801 errno = EINVAL;
802 TALLOC_FREE(frame);
803 return -1;
806 if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
807 errno = EBADF;
808 TALLOC_FREE(frame);
809 return -1;
812 if (!file->file) {
813 errno = EINVAL;
814 TALLOC_FREE(frame);
815 return -1;
818 /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
819 if (SMBC_parse_path(frame,
820 context,
821 file->fname,
822 NULL,
823 &server,
824 &port,
825 &share,
826 &path,
827 &user,
828 &password,
829 NULL)) {
830 errno = EINVAL;
831 TALLOC_FREE(frame);
832 return -1;
835 /*d_printf(">>>fstat: resolving %s\n", path);*/
836 status = cli_resolve_path(frame, "", context->internal->auth_info,
837 file->srv->cli, path,
838 &targetcli, &targetpath);
839 if (!NT_STATUS_IS_OK(status)) {
840 d_printf("Could not resolve %s\n", path);
841 errno = ENOENT;
842 TALLOC_FREE(frame);
843 return -1;
845 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
847 if (!NT_STATUS_IS_OK(cli_ftruncate(targetcli, file->cli_fd, (uint64_t)size))) {
848 errno = EINVAL;
849 TALLOC_FREE(frame);
850 return -1;
853 TALLOC_FREE(frame);
854 return 0;