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