libsmbclient: Initialize written value before use.
[vlendec/samba-autobuild/.git] / source3 / libsmb / libsmb_file.c
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             uint16_t *mode,
456             off_t *size,
457             struct timespec *create_time_ts,
458             struct timespec *access_time_ts,
459             struct timespec *write_time_ts,
460             struct timespec *change_time_ts,
461             SMB_INO_T *ino)
462 {
463         char *fixedpath = NULL;
464         char *targetpath = NULL;
465         struct cli_state *targetcli = NULL;
466         time_t write_time;
467         TALLOC_CTX *frame = talloc_stackframe();
468         NTSTATUS status;
469
470         if (!context || !context->internal->initialized) {
471                 errno = EINVAL;
472                 TALLOC_FREE(frame);
473                 return False;
474         }
475
476         /* path fixup for . and .. */
477         if (strequal(path, ".") || strequal(path, "..")) {
478                 fixedpath = talloc_strdup(frame, "\\");
479                 if (!fixedpath) {
480                         errno = ENOMEM;
481                         TALLOC_FREE(frame);
482                         return False;
483                 }
484         } else {
485                 fixedpath = talloc_strdup(frame, path);
486                 if (!fixedpath) {
487                         errno = ENOMEM;
488                         TALLOC_FREE(frame);
489                         return False;
490                 }
491                 trim_string(fixedpath, NULL, "\\..");
492                 trim_string(fixedpath, NULL, "\\.");
493         }
494         DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
495
496         status = cli_resolve_path(frame, "", context->internal->auth_info,
497                                   srv->cli, fixedpath,
498                                   &targetcli, &targetpath);
499         if (!NT_STATUS_IS_OK(status)) {
500                 d_printf("Couldn't resolve %s\n", path);
501                 errno = ENOENT;
502                 TALLOC_FREE(frame);
503                 return False;
504         }
505
506         if (!srv->no_pathinfo2 &&
507             NT_STATUS_IS_OK(cli_qpathinfo2(targetcli, targetpath,
508                            create_time_ts,
509                            access_time_ts,
510                            write_time_ts,
511                            change_time_ts,
512                            size, mode, ino))) {
513                 TALLOC_FREE(frame);
514                 return True;
515         }
516
517         srv->no_pathinfo2 = True;
518
519         if (!srv->no_pathinfo3 &&
520             NT_STATUS_IS_OK(cli_qpathinfo3(targetcli, targetpath,
521                            create_time_ts,
522                            access_time_ts,
523                            write_time_ts,
524                            change_time_ts,
525                            size, mode, ino))) {
526                 TALLOC_FREE(frame);
527                 return True;
528         }
529
530         srv->no_pathinfo3 = True;
531
532         /* if this is NT then don't bother with the getatr */
533         if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
534                 goto all_failed;
535         }
536
537         if (NT_STATUS_IS_OK(cli_getatr(targetcli, targetpath, mode, size, &write_time))) {
538                 struct timespec w_time_ts;
539
540                 w_time_ts = convert_time_t_to_timespec(write_time);
541                 if (write_time_ts != NULL) {
542                         *write_time_ts = w_time_ts;
543                 }
544                 if (create_time_ts != NULL) {
545                         *create_time_ts = w_time_ts;
546                 }
547                 if (access_time_ts != NULL) {
548                         *access_time_ts = w_time_ts;
549                 }
550                 if (change_time_ts != NULL) {
551                         *change_time_ts = w_time_ts;
552                 }
553                 if (ino) {
554                         *ino = 0;
555                 }
556                 TALLOC_FREE(frame);
557                 return True;
558         }
559
560 all_failed:
561         srv->no_pathinfo2 = False;
562         srv->no_pathinfo3 = False;
563
564         errno = EPERM;
565         TALLOC_FREE(frame);
566         return False;
567 }
568
569 /*
570  * Set file info on an SMB server.  Use setpathinfo call first.  If that
571  * fails, use setattrE..
572  *
573  * Access and modification time parameters are always used and must be
574  * provided.  Create time, if zero, will be determined from the actual create
575  * time of the file.  If non-zero, the create time will be set as well.
576  *
577  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
578  */
579 bool
580 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
581             time_t create_time,
582             time_t access_time,
583             time_t write_time,
584             time_t change_time,
585             uint16_t mode)
586 {
587         uint16_t fd;
588         int ret;
589         TALLOC_CTX *frame = talloc_stackframe();
590
591         /*
592          * First, try setpathinfo (if qpathinfo succeeded), for it is the
593          * modern function for "new code" to be using, and it works given a
594          * filename rather than requiring that the file be opened to have its
595          * attributes manipulated.
596          */
597         if (srv->no_pathinfo ||
598             !NT_STATUS_IS_OK(cli_setpathinfo_basic(srv->cli, path,
599                                                    create_time,
600                                                    access_time,
601                                                    write_time,
602                                                    change_time,
603                                                    mode))) {
604
605                 /*
606                  * setpathinfo is not supported; go to plan B.
607                  *
608                  * cli_setatr() does not work on win98, and it also doesn't
609                  * support setting the access time (only the modification
610                  * time), so in all cases, we open the specified file and use
611                  * cli_setattrE() which should work on all OS versions, and
612                  * supports both times.
613                  */
614
615                 /* Don't try {q,set}pathinfo() again, with this server */
616                 srv->no_pathinfo = True;
617
618                 /* Open the file */
619                 if (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) {
620                         errno = SMBC_errno(context, srv->cli);
621                         TALLOC_FREE(frame);
622                         return -1;
623                 }
624
625                 /* Set the new attributes */
626                 ret = NT_STATUS_IS_OK(cli_setattrE(srv->cli, fd,
627                                    change_time,
628                                    access_time,
629                                    write_time));
630
631                 /* Close the file */
632                 cli_close(srv->cli, fd);
633
634                 /*
635                  * Unfortunately, setattrE() doesn't have a provision for
636                  * setting the access mode (attributes).  We'll have to try
637                  * cli_setatr() for that, and with only this parameter, it
638                  * seems to work on win98.
639                  */
640                 if (ret && mode != (uint16_t) -1) {
641                         ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0));
642                 }
643
644                 if (! ret) {
645                         errno = SMBC_errno(context, srv->cli);
646                         TALLOC_FREE(frame);
647                         return False;
648                 }
649         }
650
651         TALLOC_FREE(frame);
652         return True;
653 }
654
655 /*
656  * A routine to lseek() a file
657  */
658
659 off_t
660 SMBC_lseek_ctx(SMBCCTX *context,
661                SMBCFILE *file,
662                off_t offset,
663                int whence)
664 {
665         off_t size;
666         TALLOC_CTX *frame = talloc_stackframe();
667
668         if (!context || !context->internal->initialized) {
669                 errno = EINVAL;
670                 TALLOC_FREE(frame);
671                 return -1;
672         }
673
674         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
675                 errno = EBADF;
676                 TALLOC_FREE(frame);
677                 return -1;
678         }
679
680         if (!file->file) {
681                 errno = EINVAL;
682                 TALLOC_FREE(frame);
683                 return -1;      /* Can't lseek a dir ... */
684         }
685
686         switch (whence) {
687         case SEEK_SET:
688                 file->offset = offset;
689                 break;
690         case SEEK_CUR:
691                 file->offset += offset;
692                 break;
693         case SEEK_END:
694                 if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
695                                              file->targetcli, file->cli_fd, NULL,
696                                              &size, NULL, NULL, NULL, NULL,
697                                              NULL))) {
698                         off_t b_size = size;
699                         if (!NT_STATUS_IS_OK(cli_getattrE(file->targetcli, file->cli_fd,
700                                           NULL, &b_size, NULL, NULL, NULL))) {
701                                 errno = EINVAL;
702                                 TALLOC_FREE(frame);
703                                 return -1;
704                         } else
705                                 size = b_size;
706                 }
707                 file->offset = size + offset;
708                 break;
709         default:
710                 errno = EINVAL;
711                 break;
712         }
713
714         TALLOC_FREE(frame);
715         return file->offset;
716 }
717
718
719 /*
720  * Routine to truncate a file given by its file descriptor, to a specified size
721  */
722
723 int
724 SMBC_ftruncate_ctx(SMBCCTX *context,
725                    SMBCFILE *file,
726                    off_t length)
727 {
728         off_t size = length;
729         TALLOC_CTX *frame = talloc_stackframe();
730
731         if (!context || !context->internal->initialized) {
732                 errno = EINVAL;
733                 TALLOC_FREE(frame);
734                 return -1;
735         }
736
737         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
738                 errno = EBADF;
739                 TALLOC_FREE(frame);
740                 return -1;
741         }
742
743         if (!file->file) {
744                 errno = EINVAL;
745                 TALLOC_FREE(frame);
746                 return -1;
747         }
748
749         if (!NT_STATUS_IS_OK(cli_ftruncate(file->targetcli, file->cli_fd, (uint64_t)size))) {
750                 errno = EINVAL;
751                 TALLOC_FREE(frame);
752                 return -1;
753         }
754
755         TALLOC_FREE(frame);
756         return 0;
757 }