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
10 
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.
15 
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.
20 
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/>.
23 */
24 
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"
30 
31 /*
32  * Routine to open() a file ...
33  */
34 
35 SMBCFILE *
36 SMBC_open_ctx(SMBCCTX *context,
37               const char *fname,
38               int flags,
39               mode_t mode)
40 {
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();
55 
56 	if (!context || !context->internal->initialized) {
57 		errno = EINVAL;  /* Best I can think of ... */
58 		TALLOC_FREE(frame);
59 		return NULL;
60 	}
61 
62 	if (!fname) {
63 		errno = EINVAL;
64 		TALLOC_FREE(frame);
65 		return NULL;
66 	}
67 
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;
82         }
83 
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;
90 		}
91 	}
92 
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 */
99 	}
100 
101 	/* Hmmm, the test for a directory is suspect here ... FIXME */
102 
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;
111 		}
112 
113 		ZERO_STRUCTP(file);
114 
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;
125 		}
126 		/*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
127 
128 		status = cli_open(targetcli, targetpath, flags,
129                                    context->internal->share_mode, &fd);
130 		if (!NT_STATUS_IS_OK(status)) {
131 
132 			/* Handle the error ... */
133 
134 			SAFE_FREE(file);
135 			errno = SMBC_errno(context, targetcli);
136 			TALLOC_FREE(frame);
137 			return NULL;
138 		}
139 
140 		/* Fill in file struct */
141 
142 		file->cli_fd  = fd;
143 		file->fname   = SMB_STRDUP(fname);
144 		file->srv     = srv;
145 		file->offset  = 0;
146 		file->file    = True;
147 		/*
148 		 * targetcli is either equal to srv->cli or
149 		 * is a subsidiary DFS connection. Either way
150 		 * file->cli_fd belongs to it so we must cache
151 		 * it for read/write/close, not re-resolve each time.
152 		 * Re-resolving is both slow and incorrect.
153 		 */
154 		file->targetcli = targetcli;
155 
156 		DLIST_ADD(context->internal->files, file);
157 
158                 /*
159                  * If the file was opened in O_APPEND mode, all write
160                  * operations should be appended to the file.  To do that,
161                  * though, using this protocol, would require a getattrE()
162                  * call for each and every write, to determine where the end
163                  * of the file is. (There does not appear to be an append flag
164                  * in the protocol.)  Rather than add all of that overhead of
165                  * retrieving the current end-of-file offset prior to each
166                  * write operation, we'll assume that most append operations
167                  * will continuously write, so we'll just set the offset to
168                  * the end of the file now and hope that's adequate.
169                  *
170                  * Note to self: If this proves inadequate, and O_APPEND
171                  * should, in some cases, be forced for each write, add a
172                  * field in the context options structure, for
173                  * "strict_append_mode" which would select between the current
174                  * behavior (if FALSE) or issuing a getattrE() prior to each
175                  * write and forcing the write to the end of the file (if
176                  * TRUE).  Adding that capability will likely require adding
177                  * an "append" flag into the _SMBCFILE structure to track
178                  * whether a file was opened in O_APPEND mode.  -- djl
179                  */
180                 if (flags & O_APPEND) {
181                         if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
182                                 (void) SMBC_close_ctx(context, file);
183                                 errno = ENXIO;
184 				TALLOC_FREE(frame);
185                                 return NULL;
186                         }
187                 }
188 
189 		TALLOC_FREE(frame);
190 		return file;
191 	}
192 
193 	/* Check if opendir needed ... */
194 
195 	if (!NT_STATUS_IS_OK(status)) {
196 		int eno = 0;
197 
198 		eno = SMBC_errno(context, srv->cli);
199 		file = smbc_getFunctionOpendir(context)(context, fname);
200 		if (!file) errno = eno;
201 		TALLOC_FREE(frame);
202 		return file;
203 	}
204 
205 	errno = EINVAL; /* FIXME, correct errno ? */
206 	TALLOC_FREE(frame);
207 	return NULL;
208 }
209 
210 /*
211  * Routine to create a file
212  */
213 
214 SMBCFILE *
215 SMBC_creat_ctx(SMBCCTX *context,
216                const char *path,
217                mode_t mode)
218 {
219 	if (!context || !context->internal->initialized) {
220 		errno = EINVAL;
221 		return NULL;
222 	}
223 
224 	return SMBC_open_ctx(context, path,
225                              O_WRONLY | O_CREAT | O_TRUNC, mode);
226 }
227 
228 /*
229  * Routine to read() a file ...
230  */
231 
232 ssize_t
233 SMBC_read_ctx(SMBCCTX *context,
234               SMBCFILE *file,
235               void *buf,
236               size_t count)
237 {
238 	size_t ret;
239 	TALLOC_CTX *frame = talloc_stackframe();
240 	NTSTATUS status;
241 
242         /*
243          * offset:
244          *
245          * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
246          * appears to pass file->offset (which is type off_t) differently than
247          * a local variable of type off_t.  Using local variable "offset" in
248          * the call to cli_read() instead of file->offset fixes a problem
249          * retrieving data at an offset greater than 4GB.
250          */
251         off_t offset;
252 
253 	if (!context || !context->internal->initialized) {
254 		errno = EINVAL;
255 		TALLOC_FREE(frame);
256 		return -1;
257 	}
258 
259 	DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
260 
261 	if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
262 		errno = EBADF;
263 		TALLOC_FREE(frame);
264 		return -1;
265 	}
266 
267 	offset = file->offset;
268 
269 	/* Check that the buffer exists ... */
270 
271 	if (buf == NULL) {
272 		errno = EINVAL;
273 		TALLOC_FREE(frame);
274 		return -1;
275 	}
276 
277 	status = cli_read(file->targetcli, file->cli_fd, (char *)buf, offset,
278 			  count, &ret);
279 	if (!NT_STATUS_IS_OK(status)) {
280 		errno = SMBC_errno(context, file->targetcli);
281 		TALLOC_FREE(frame);
282 		return -1;
283 	}
284 
285 	file->offset += ret;
286 
287 	DEBUG(4, ("  --> %ld\n", (unsigned long)ret));
288 
289 	TALLOC_FREE(frame);
290 	return ret;  /* Success, ret bytes of data ... */
291 }
292 
293 off_t
294 SMBC_splice_ctx(SMBCCTX *context,
295                 SMBCFILE *srcfile,
296                 SMBCFILE *dstfile,
297                 off_t count,
298                 int (*splice_cb)(off_t n, void *priv),
299                 void *priv)
300 {
301 	off_t written = 0;
302 	TALLOC_CTX *frame = talloc_stackframe();
303 	NTSTATUS status;
304 
305 	if (!context || !context->internal->initialized) {
306 		errno = EINVAL;
307 		TALLOC_FREE(frame);
308 		return -1;
309 	}
310 
311 	if (!srcfile ||
312 	    !SMBC_dlist_contains(context->internal->files, srcfile))
313 	{
314 		errno = EBADF;
315 		TALLOC_FREE(frame);
316 		return -1;
317 	}
318 
319 	if (!dstfile ||
320 	    !SMBC_dlist_contains(context->internal->files, dstfile))
321 	{
322 		errno = EBADF;
323 		TALLOC_FREE(frame);
324 		return -1;
325 	}
326 
327 	status = cli_splice(srcfile->targetcli, dstfile->targetcli,
328 			    srcfile->cli_fd, dstfile->cli_fd,
329 			    count, srcfile->offset, dstfile->offset, &written,
330 			    splice_cb, priv);
331 	if (!NT_STATUS_IS_OK(status)) {
332 		errno = SMBC_errno(context, srcfile->targetcli);
333 		TALLOC_FREE(frame);
334 		return -1;
335 	}
336 
337 	srcfile->offset += written;
338 	dstfile->offset += written;
339 
340 	TALLOC_FREE(frame);
341 	return written;
342 }
343 
344 /*
345  * Routine to write() a file ...
346  */
347 
348 ssize_t
349 SMBC_write_ctx(SMBCCTX *context,
350                SMBCFILE *file,
351                const void *buf,
352                size_t count)
353 {
354         off_t offset;
355 	TALLOC_CTX *frame = talloc_stackframe();
356 	NTSTATUS status;
357 
358 	/* First check all pointers before dereferencing them */
359 
360 	if (!context || !context->internal->initialized) {
361 		errno = EINVAL;
362 		TALLOC_FREE(frame);
363 		return -1;
364 	}
365 
366 	if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
367 		errno = EBADF;
368 		TALLOC_FREE(frame);
369 		return -1;
370 	}
371 
372 	/* Check that the buffer exists ... */
373 
374 	if (buf == NULL) {
375 		errno = EINVAL;
376 		TALLOC_FREE(frame);
377 		return -1;
378 	}
379 
380         offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
381 
382 	status = cli_writeall(file->targetcli, file->cli_fd,
383 			      0, (const uint8_t *)buf, offset, count, NULL);
384 	if (!NT_STATUS_IS_OK(status)) {
385 		errno = map_errno_from_nt_status(status);
386 		TALLOC_FREE(frame);
387 		return -1;
388 	}
389 
390 	file->offset += count;
391 
392 	TALLOC_FREE(frame);
393 	return count;  /* Success, 0 bytes of data ... */
394 }
395 
396 /*
397  * Routine to close() a file ...
398  */
399 
400 int
401 SMBC_close_ctx(SMBCCTX *context,
402                SMBCFILE *file)
403 {
404 	TALLOC_CTX *frame = talloc_stackframe();
405 
406 	if (!context || !context->internal->initialized) {
407 		errno = EINVAL;
408 		TALLOC_FREE(frame);
409 		return -1;
410 	}
411 
412 	if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
413 		errno = EBADF;
414 		TALLOC_FREE(frame);
415 		return -1;
416 	}
417 
418 	/* IS a dir ... */
419 	if (!file->file) {
420 		TALLOC_FREE(frame);
421 		return smbc_getFunctionClosedir(context)(context, file);
422 	}
423 
424 	if (!NT_STATUS_IS_OK(cli_close(file->targetcli, file->cli_fd))) {
425 		SMBCSRV *srv;
426 		DEBUG(3, ("cli_close failed on %s. purging server.\n",
427 			  file->fname));
428 		/* Deallocate slot and remove the server
429 		 * from the server cache if unused */
430 		errno = SMBC_errno(context, file->targetcli);
431 		srv = file->srv;
432 		DLIST_REMOVE(context->internal->files, file);
433 		SAFE_FREE(file->fname);
434 		SAFE_FREE(file);
435 		smbc_getFunctionRemoveUnusedServer(context)(context, srv);
436 		TALLOC_FREE(frame);
437 		return -1;
438 	}
439 
440 	DLIST_REMOVE(context->internal->files, file);
441 	SAFE_FREE(file->fname);
442 	SAFE_FREE(file);
443 	TALLOC_FREE(frame);
444 	return 0;
445 }
446 
447 /*
448  * Get info from an SMB server on a file. Use a qpathinfo call first
449  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
450  */
451 bool
452 SMBC_getatr(SMBCCTX * context,
453             SMBCSRV *srv,
454             const char *path,
455 	    struct stat *sb)
456 {
457 	char *fixedpath = NULL;
458 	char *targetpath = NULL;
459 	struct cli_state *targetcli = NULL;
460 	uint16_t mode = 0;
461 	off_t size = 0;
462 	struct timespec create_time_ts = {0};
463 	struct timespec access_time_ts = {0};
464 	struct timespec write_time_ts = {0};
465 	struct timespec change_time_ts = {0};
466 	time_t write_time = 0;
467 	SMB_INO_T ino = 0;
468 	TALLOC_CTX *frame = talloc_stackframe();
469 	NTSTATUS status;
470 
471 	if (!context || !context->internal->initialized) {
472 		errno = EINVAL;
473 		TALLOC_FREE(frame);
474  		return False;
475  	}
476 
477 	/* path fixup for . and .. */
478 	if (strequal(path, ".") || strequal(path, "..")) {
479 		fixedpath = talloc_strdup(frame, "\\");
480 		if (!fixedpath) {
481 			errno = ENOMEM;
482 			TALLOC_FREE(frame);
483 			return False;
484 		}
485 	} else {
486 		fixedpath = talloc_strdup(frame, path);
487 		if (!fixedpath) {
488 			errno = ENOMEM;
489 			TALLOC_FREE(frame);
490 			return False;
491 		}
492 		trim_string(fixedpath, NULL, "\\..");
493 		trim_string(fixedpath, NULL, "\\.");
494 	}
495 	DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
496 
497 	status = cli_resolve_path(frame, "", context->internal->auth_info,
498 				  srv->cli, fixedpath,
499 				  &targetcli, &targetpath);
500 	if (!NT_STATUS_IS_OK(status)) {
501 		d_printf("Couldn't resolve %s\n", path);
502                 errno = ENOENT;
503 		TALLOC_FREE(frame);
504 		return False;
505 	}
506 
507 	if (!srv->no_pathinfo2) {
508 		status = cli_qpathinfo2(targetcli,
509 					targetpath,
510 					&create_time_ts,
511 					&access_time_ts,
512 					&write_time_ts,
513 					&change_time_ts,
514 					&size,
515 					&mode,
516 					&ino);
517 		if (NT_STATUS_IS_OK(status)) {
518 			goto setup_stat;
519 		}
520         }
521 
522 	srv->no_pathinfo2 = True;
523 
524 	if (!srv->no_pathinfo3) {
525 		status = cli_qpathinfo3(targetcli,
526 					targetpath,
527 					&create_time_ts,
528 					&access_time_ts,
529 					&write_time_ts,
530 					&change_time_ts,
531 					&size,
532 					&mode,
533 					&ino);
534 		if (NT_STATUS_IS_OK(status)) {
535 			goto setup_stat;
536 		}
537         }
538 
539 	srv->no_pathinfo3 = True;
540 
541 	/* if this is NT then don't bother with the getatr */
542 	if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
543 		goto all_failed;
544         }
545 
546 	status = cli_getatr(targetcli, targetpath, &mode, &size, &write_time);
547 	if (NT_STATUS_IS_OK(status)) {
548 		struct timespec w_time_ts =
549 			convert_time_t_to_timespec(write_time);
550 
551 		access_time_ts = change_time_ts = write_time_ts = w_time_ts;
552 
553 		goto setup_stat;
554 	}
555 
556 setup_stat:
557 	setup_stat(sb,
558 		   path,
559 		   size,
560 		   mode,
561 		   ino,
562 		   srv->dev,
563 		   access_time_ts,
564 		   change_time_ts,
565 		   write_time_ts);
566 
567 	TALLOC_FREE(frame);
568 	return true;
569 
570 all_failed:
571 	srv->no_pathinfo2 = False;
572 	srv->no_pathinfo3 = False;
573 
574         errno = EPERM;
575 	TALLOC_FREE(frame);
576 	return False;
577 }
578 
579 /*
580  * Set file info on an SMB server.  Use setpathinfo call first.  If that
581  * fails, use setattrE..
582  *
583  * Access and modification time parameters are always used and must be
584  * provided.  Create time, if zero, will be determined from the actual create
585  * time of the file.  If non-zero, the create time will be set as well.
586  *
587  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
588  */
589 bool
590 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
591             time_t create_time,
592             time_t access_time,
593             time_t write_time,
594             time_t change_time,
595             uint16_t mode)
596 {
597         uint16_t fd;
598         int ret;
599 	TALLOC_CTX *frame = talloc_stackframe();
600 
601         /*
602          * First, try setpathinfo (if qpathinfo succeeded), for it is the
603          * modern function for "new code" to be using, and it works given a
604          * filename rather than requiring that the file be opened to have its
605          * attributes manipulated.
606          */
607         if (srv->no_pathinfo ||
608             !NT_STATUS_IS_OK(cli_setpathinfo_basic(srv->cli, path,
609 						   create_time,
610 						   access_time,
611 						   write_time,
612 						   change_time,
613 						   mode))) {
614 
615                 /*
616                  * setpathinfo is not supported; go to plan B.
617                  *
618                  * cli_setatr() does not work on win98, and it also doesn't
619                  * support setting the access time (only the modification
620                  * time), so in all cases, we open the specified file and use
621                  * cli_setattrE() which should work on all OS versions, and
622                  * supports both times.
623                  */
624 
625                 /* Don't try {q,set}pathinfo() again, with this server */
626                 srv->no_pathinfo = True;
627 
628                 /* Open the file */
629                 if (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) {
630                         errno = SMBC_errno(context, srv->cli);
631 			TALLOC_FREE(frame);
632                         return False;
633                 }
634 
635                 /* Set the new attributes */
636                 ret = NT_STATUS_IS_OK(cli_setattrE(srv->cli, fd,
637                                    change_time,
638                                    access_time,
639                                    write_time));
640 
641                 /* Close the file */
642                 cli_close(srv->cli, fd);
643 
644                 /*
645                  * Unfortunately, setattrE() doesn't have a provision for
646                  * setting the access mode (attributes).  We'll have to try
647                  * cli_setatr() for that, and with only this parameter, it
648                  * seems to work on win98.
649                  */
650                 if (ret && mode != (uint16_t) -1) {
651                         ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0));
652                 }
653 
654                 if (! ret) {
655                         errno = SMBC_errno(context, srv->cli);
656 			TALLOC_FREE(frame);
657                         return False;
658                 }
659         }
660 
661 	TALLOC_FREE(frame);
662         return True;
663 }
664 
665 /*
666  * A routine to lseek() a file
667  */
668 
669 off_t
670 SMBC_lseek_ctx(SMBCCTX *context,
671                SMBCFILE *file,
672                off_t offset,
673                int whence)
674 {
675 	off_t size;
676 	TALLOC_CTX *frame = talloc_stackframe();
677 
678 	if (!context || !context->internal->initialized) {
679 		errno = EINVAL;
680 		TALLOC_FREE(frame);
681 		return -1;
682 	}
683 
684 	if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
685 		errno = EBADF;
686 		TALLOC_FREE(frame);
687 		return -1;
688 	}
689 
690 	if (!file->file) {
691 		errno = EINVAL;
692 		TALLOC_FREE(frame);
693 		return -1;      /* Can't lseek a dir ... */
694 	}
695 
696 	switch (whence) {
697 	case SEEK_SET:
698 		file->offset = offset;
699 		break;
700 	case SEEK_CUR:
701 		file->offset += offset;
702 		break;
703 	case SEEK_END:
704 		if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
705 					     file->targetcli, file->cli_fd, NULL,
706 					     &size, NULL, NULL, NULL, NULL,
707 					     NULL))) {
708                         off_t b_size = size;
709 			if (!NT_STATUS_IS_OK(cli_getattrE(file->targetcli, file->cli_fd,
710                                           NULL, &b_size, NULL, NULL, NULL))) {
711                                 errno = EINVAL;
712                                 TALLOC_FREE(frame);
713                                 return -1;
714                         } else
715                                 size = b_size;
716 		}
717 		file->offset = size + offset;
718 		break;
719 	default:
720 		errno = EINVAL;
721 		break;
722 	}
723 
724 	TALLOC_FREE(frame);
725 	return file->offset;
726 }
727 
728 
729 /*
730  * Routine to truncate a file given by its file descriptor, to a specified size
731  */
732 
733 int
734 SMBC_ftruncate_ctx(SMBCCTX *context,
735                    SMBCFILE *file,
736                    off_t length)
737 {
738 	off_t size = length;
739 	TALLOC_CTX *frame = talloc_stackframe();
740 
741 	if (!context || !context->internal->initialized) {
742 		errno = EINVAL;
743 		TALLOC_FREE(frame);
744 		return -1;
745 	}
746 
747 	if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
748 		errno = EBADF;
749 		TALLOC_FREE(frame);
750 		return -1;
751 	}
752 
753 	if (!file->file) {
754 		errno = EINVAL;
755 		TALLOC_FREE(frame);
756 		return -1;
757 	}
758 
759         if (!NT_STATUS_IS_OK(cli_ftruncate(file->targetcli, file->cli_fd, (uint64_t)size))) {
760                 errno = EINVAL;
761                 TALLOC_FREE(frame);
762                 return -1;
763         }
764 
765 	TALLOC_FREE(frame);
766 	return 0;
767 }
768