s3: libsmbclient: Add server-side copy support
[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                 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                 DLIST_ADD(context->internal->files, file);
149
150                 /*
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.
161                  *
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
171                  */
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;
178                         }
179                 }
180
181                 TALLOC_FREE(frame);
182                 return file;
183         }
184
185         /* Check if opendir needed ... */
186
187         if (!NT_STATUS_IS_OK(status)) {
188                 int eno = 0;
189
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;
195         }
196
197         errno = EINVAL; /* FIXME, correct errno ? */
198         TALLOC_FREE(frame);
199         return NULL;
200 }
201
202 /*
203  * Routine to create a file
204  */
205
206 SMBCFILE *
207 SMBC_creat_ctx(SMBCCTX *context,
208                const char *path,
209                mode_t mode)
210 {
211         if (!context || !context->internal->initialized) {
212                 errno = EINVAL;
213                 return NULL;
214         }
215
216         return SMBC_open_ctx(context, path,
217                              O_WRONLY | O_CREAT | O_TRUNC, mode);
218 }
219
220 /*
221  * Routine to read() a file ...
222  */
223
224 ssize_t
225 SMBC_read_ctx(SMBCCTX *context,
226               SMBCFILE *file,
227               void *buf,
228               size_t count)
229 {
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;
238
239         /*
240          * offset:
241          *
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.
247          */
248         off_t offset;
249
250         if (!context || !context->internal->initialized) {
251                 errno = EINVAL;
252                 TALLOC_FREE(frame);
253                 return -1;
254         }
255
256         DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
257
258         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
259                 errno = EBADF;
260                 TALLOC_FREE(frame);
261                 return -1;
262         }
263
264         offset = file->offset;
265
266         /* Check that the buffer exists ... */
267
268         if (buf == NULL) {
269                 errno = EINVAL;
270                 TALLOC_FREE(frame);
271                 return -1;
272         }
273
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;
289         }
290
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;
300         }
301         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
302
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;
309         }
310
311         file->offset += ret;
312
313         DEBUG(4, ("  --> %ld\n", (unsigned long)ret));
314
315         TALLOC_FREE(frame);
316         return ret;  /* Success, ret bytes of data ... */
317 }
318
319 off_t
320 SMBC_splice_ctx(SMBCCTX *context,
321                 SMBCFILE *srcfile,
322                 SMBCFILE *dstfile,
323                 off_t count,
324                 int (*splice_cb)(off_t n, void *priv),
325                 void *priv)
326 {
327         off_t written;
328         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
329         char *path = NULL;
330         char *targetpath = NULL;
331         struct cli_state *srccli = NULL;
332         struct cli_state *dstcli = NULL;
333         uint16_t port = 0;
334         TALLOC_CTX *frame = talloc_stackframe();
335         NTSTATUS status;
336
337         if (!context || !context->internal->initialized) {
338                 errno = EINVAL;
339                 TALLOC_FREE(frame);
340                 return -1;
341         }
342
343         if (!srcfile ||
344             !SMBC_dlist_contains(context->internal->files, srcfile))
345         {
346                 errno = EBADF;
347                 TALLOC_FREE(frame);
348                 return -1;
349         }
350
351         if (!dstfile ||
352             !SMBC_dlist_contains(context->internal->files, dstfile))
353         {
354                 errno = EBADF;
355                 TALLOC_FREE(frame);
356                 return -1;
357         }
358
359         if (SMBC_parse_path(frame,
360                             context,
361                             srcfile->fname,
362                             NULL,
363                             &server,
364                             &port,
365                             &share,
366                             &path,
367                             &user,
368                             &password,
369                             NULL)) {
370                 errno = EINVAL;
371                 TALLOC_FREE(frame);
372                 return -1;
373         }
374
375         status = cli_resolve_path(frame, "", context->internal->auth_info,
376                                   srcfile->srv->cli, path,
377                                   &srccli, &targetpath);
378         if (!NT_STATUS_IS_OK(status)) {
379                 d_printf("Could not resolve %s\n", path);
380                 errno = ENOENT;
381                 TALLOC_FREE(frame);
382                 return -1;
383         }
384
385         if (SMBC_parse_path(frame,
386                             context,
387                             dstfile->fname,
388                             NULL,
389                             &server,
390                             &port,
391                             &share,
392                             &path,
393                             &user,
394                             &password,
395                             NULL)) {
396                 errno = EINVAL;
397                 TALLOC_FREE(frame);
398                 return -1;
399         }
400
401         status = cli_resolve_path(frame, "", context->internal->auth_info,
402                                   dstfile->srv->cli, path,
403                                   &dstcli, &targetpath);
404         if (!NT_STATUS_IS_OK(status)) {
405                 d_printf("Could not resolve %s\n", path);
406                 errno = ENOENT;
407                 TALLOC_FREE(frame);
408                 return -1;
409         }
410
411         status = cli_splice(srccli, dstcli,
412                             srcfile->cli_fd, dstfile->cli_fd,
413                             count, srcfile->offset, dstfile->offset, &written,
414                             splice_cb, priv);
415         if (!NT_STATUS_IS_OK(status)) {
416                 errno = SMBC_errno(context, srccli);
417                 TALLOC_FREE(frame);
418                 return -1;
419         }
420
421         srcfile->offset += written;
422         dstfile->offset += written;
423
424         TALLOC_FREE(frame);
425         return written;
426 }
427
428 /*
429  * Routine to write() a file ...
430  */
431
432 ssize_t
433 SMBC_write_ctx(SMBCCTX *context,
434                SMBCFILE *file,
435                const void *buf,
436                size_t count)
437 {
438         off_t offset;
439         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
440         char *path = NULL;
441         char *targetpath = NULL;
442         struct cli_state *targetcli = NULL;
443         uint16_t port = 0;
444         TALLOC_CTX *frame = talloc_stackframe();
445         NTSTATUS status;
446
447         /* First check all pointers before dereferencing them */
448
449         if (!context || !context->internal->initialized) {
450                 errno = EINVAL;
451                 TALLOC_FREE(frame);
452                 return -1;
453         }
454
455         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
456                 errno = EBADF;
457                 TALLOC_FREE(frame);
458                 return -1;
459         }
460
461         /* Check that the buffer exists ... */
462
463         if (buf == NULL) {
464                 errno = EINVAL;
465                 TALLOC_FREE(frame);
466                 return -1;
467         }
468
469         offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
470
471         /*d_printf(">>>write: parsing %s\n", file->fname);*/
472         if (SMBC_parse_path(frame,
473                             context,
474                             file->fname,
475                             NULL,
476                             &server,
477                             &port,
478                             &share,
479                             &path,
480                             &user,
481                             &password,
482                             NULL)) {
483                 errno = EINVAL;
484                 TALLOC_FREE(frame);
485                 return -1;
486         }
487
488         /*d_printf(">>>write: resolving %s\n", path);*/
489         status = cli_resolve_path(frame, "", context->internal->auth_info,
490                                   file->srv->cli, path,
491                                   &targetcli, &targetpath);
492         if (!NT_STATUS_IS_OK(status)) {
493                 d_printf("Could not resolve %s\n", path);
494                 errno = ENOENT;
495                 TALLOC_FREE(frame);
496                 return -1;
497         }
498         /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
499
500         status = cli_writeall(targetcli, file->cli_fd,
501                               0, (const uint8_t *)buf, offset, count, NULL);
502         if (!NT_STATUS_IS_OK(status)) {
503                 errno = map_errno_from_nt_status(status);
504                 TALLOC_FREE(frame);
505                 return -1;
506         }
507
508         file->offset += count;
509
510         TALLOC_FREE(frame);
511         return count;  /* Success, 0 bytes of data ... */
512 }
513
514 /*
515  * Routine to close() a file ...
516  */
517
518 int
519 SMBC_close_ctx(SMBCCTX *context,
520                SMBCFILE *file)
521 {
522         SMBCSRV *srv;
523         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
524         char *path = NULL;
525         char *targetpath = NULL;
526         uint16_t port = 0;
527         struct cli_state *targetcli = NULL;
528         TALLOC_CTX *frame = talloc_stackframe();
529         NTSTATUS status;
530
531         if (!context || !context->internal->initialized) {
532                 errno = EINVAL;
533                 TALLOC_FREE(frame);
534                 return -1;
535         }
536
537         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
538                 errno = EBADF;
539                 TALLOC_FREE(frame);
540                 return -1;
541         }
542
543         /* IS a dir ... */
544         if (!file->file) {
545                 TALLOC_FREE(frame);
546                 return smbc_getFunctionClosedir(context)(context, file);
547         }
548
549         /*d_printf(">>>close: parsing %s\n", file->fname);*/
550         if (SMBC_parse_path(frame,
551                             context,
552                             file->fname,
553                             NULL,
554                             &server,
555                             &port,
556                             &share,
557                             &path,
558                             &user,
559                             &password,
560                             NULL)) {
561                 errno = EINVAL;
562                 TALLOC_FREE(frame);
563                 return -1;
564         }
565
566         /*d_printf(">>>close: resolving %s\n", path);*/
567         status = cli_resolve_path(frame, "", context->internal->auth_info,
568                                   file->srv->cli, path,
569                                   &targetcli, &targetpath);
570         if (!NT_STATUS_IS_OK(status)) {
571                 d_printf("Could not resolve %s\n", path);
572                 errno = ENOENT;
573                 TALLOC_FREE(frame);
574                 return -1;
575         }
576         /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
577
578         if (!NT_STATUS_IS_OK(cli_close(targetcli, file->cli_fd))) {
579                 DEBUG(3, ("cli_close failed on %s. purging server.\n",
580                           file->fname));
581                 /* Deallocate slot and remove the server
582                  * from the server cache if unused */
583                 errno = SMBC_errno(context, targetcli);
584                 srv = file->srv;
585                 DLIST_REMOVE(context->internal->files, file);
586                 SAFE_FREE(file->fname);
587                 SAFE_FREE(file);
588                 smbc_getFunctionRemoveUnusedServer(context)(context, srv);
589                 TALLOC_FREE(frame);
590                 return -1;
591         }
592
593         DLIST_REMOVE(context->internal->files, file);
594         SAFE_FREE(file->fname);
595         SAFE_FREE(file);
596         TALLOC_FREE(frame);
597         return 0;
598 }
599
600 /*
601  * Get info from an SMB server on a file. Use a qpathinfo call first
602  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
603  */
604 bool
605 SMBC_getatr(SMBCCTX * context,
606             SMBCSRV *srv,
607             const char *path,
608             uint16_t *mode,
609             off_t *size,
610             struct timespec *create_time_ts,
611             struct timespec *access_time_ts,
612             struct timespec *write_time_ts,
613             struct timespec *change_time_ts,
614             SMB_INO_T *ino)
615 {
616         char *fixedpath = NULL;
617         char *targetpath = NULL;
618         struct cli_state *targetcli = NULL;
619         time_t write_time;
620         TALLOC_CTX *frame = talloc_stackframe();
621         NTSTATUS status;
622
623         if (!context || !context->internal->initialized) {
624                 errno = EINVAL;
625                 TALLOC_FREE(frame);
626                 return False;
627         }
628
629         /* path fixup for . and .. */
630         if (strequal(path, ".") || strequal(path, "..")) {
631                 fixedpath = talloc_strdup(frame, "\\");
632                 if (!fixedpath) {
633                         errno = ENOMEM;
634                         TALLOC_FREE(frame);
635                         return False;
636                 }
637         } else {
638                 fixedpath = talloc_strdup(frame, path);
639                 if (!fixedpath) {
640                         errno = ENOMEM;
641                         TALLOC_FREE(frame);
642                         return False;
643                 }
644                 trim_string(fixedpath, NULL, "\\..");
645                 trim_string(fixedpath, NULL, "\\.");
646         }
647         DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
648
649         status = cli_resolve_path(frame, "", context->internal->auth_info,
650                                   srv->cli, fixedpath,
651                                   &targetcli, &targetpath);
652         if (!NT_STATUS_IS_OK(status)) {
653                 d_printf("Couldn't resolve %s\n", path);
654                 errno = ENOENT;
655                 TALLOC_FREE(frame);
656                 return False;
657         }
658
659         if (!srv->no_pathinfo2 &&
660             NT_STATUS_IS_OK(cli_qpathinfo2(targetcli, targetpath,
661                            create_time_ts,
662                            access_time_ts,
663                            write_time_ts,
664                            change_time_ts,
665                            size, mode, ino))) {
666                 TALLOC_FREE(frame);
667                 return True;
668         }
669
670         srv->no_pathinfo2 = True;
671
672         if (!srv->no_pathinfo3 &&
673             NT_STATUS_IS_OK(cli_qpathinfo3(targetcli, targetpath,
674                            create_time_ts,
675                            access_time_ts,
676                            write_time_ts,
677                            change_time_ts,
678                            size, mode, ino))) {
679                 TALLOC_FREE(frame);
680                 return True;
681         }
682
683         srv->no_pathinfo3 = True;
684
685         /* if this is NT then don't bother with the getatr */
686         if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
687                 goto all_failed;
688         }
689
690         if (NT_STATUS_IS_OK(cli_getatr(targetcli, targetpath, mode, size, &write_time))) {
691                 struct timespec w_time_ts;
692
693                 w_time_ts = convert_time_t_to_timespec(write_time);
694                 if (write_time_ts != NULL) {
695                         *write_time_ts = w_time_ts;
696                 }
697                 if (create_time_ts != NULL) {
698                         *create_time_ts = w_time_ts;
699                 }
700                 if (access_time_ts != NULL) {
701                         *access_time_ts = w_time_ts;
702                 }
703                 if (change_time_ts != NULL) {
704                         *change_time_ts = w_time_ts;
705                 }
706                 if (ino) {
707                         *ino = 0;
708                 }
709                 TALLOC_FREE(frame);
710                 return True;
711         }
712
713 all_failed:
714         srv->no_pathinfo2 = False;
715         srv->no_pathinfo3 = False;
716
717         errno = EPERM;
718         TALLOC_FREE(frame);
719         return False;
720 }
721
722 /*
723  * Set file info on an SMB server.  Use setpathinfo call first.  If that
724  * fails, use setattrE..
725  *
726  * Access and modification time parameters are always used and must be
727  * provided.  Create time, if zero, will be determined from the actual create
728  * time of the file.  If non-zero, the create time will be set as well.
729  *
730  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
731  */
732 bool
733 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
734             time_t create_time,
735             time_t access_time,
736             time_t write_time,
737             time_t change_time,
738             uint16_t mode)
739 {
740         uint16_t fd;
741         int ret;
742         TALLOC_CTX *frame = talloc_stackframe();
743
744         /*
745          * First, try setpathinfo (if qpathinfo succeeded), for it is the
746          * modern function for "new code" to be using, and it works given a
747          * filename rather than requiring that the file be opened to have its
748          * attributes manipulated.
749          */
750         if (srv->no_pathinfo ||
751             !NT_STATUS_IS_OK(cli_setpathinfo_basic(srv->cli, path,
752                                                    create_time,
753                                                    access_time,
754                                                    write_time,
755                                                    change_time,
756                                                    mode))) {
757
758                 /*
759                  * setpathinfo is not supported; go to plan B.
760                  *
761                  * cli_setatr() does not work on win98, and it also doesn't
762                  * support setting the access time (only the modification
763                  * time), so in all cases, we open the specified file and use
764                  * cli_setattrE() which should work on all OS versions, and
765                  * supports both times.
766                  */
767
768                 /* Don't try {q,set}pathinfo() again, with this server */
769                 srv->no_pathinfo = True;
770
771                 /* Open the file */
772                 if (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) {
773                         errno = SMBC_errno(context, srv->cli);
774                         TALLOC_FREE(frame);
775                         return -1;
776                 }
777
778                 /* Set the new attributes */
779                 ret = NT_STATUS_IS_OK(cli_setattrE(srv->cli, fd,
780                                    change_time,
781                                    access_time,
782                                    write_time));
783
784                 /* Close the file */
785                 cli_close(srv->cli, fd);
786
787                 /*
788                  * Unfortunately, setattrE() doesn't have a provision for
789                  * setting the access mode (attributes).  We'll have to try
790                  * cli_setatr() for that, and with only this parameter, it
791                  * seems to work on win98.
792                  */
793                 if (ret && mode != (uint16_t) -1) {
794                         ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0));
795                 }
796
797                 if (! ret) {
798                         errno = SMBC_errno(context, srv->cli);
799                         TALLOC_FREE(frame);
800                         return False;
801                 }
802         }
803
804         TALLOC_FREE(frame);
805         return True;
806 }
807
808 /*
809  * A routine to lseek() a file
810  */
811
812 off_t
813 SMBC_lseek_ctx(SMBCCTX *context,
814                SMBCFILE *file,
815                off_t offset,
816                int whence)
817 {
818         off_t size;
819         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
820         char *path = NULL;
821         char *targetpath = NULL;
822         struct cli_state *targetcli = NULL;
823         uint16_t port = 0;
824         TALLOC_CTX *frame = talloc_stackframe();
825         NTSTATUS status;
826
827         if (!context || !context->internal->initialized) {
828                 errno = EINVAL;
829                 TALLOC_FREE(frame);
830                 return -1;
831         }
832
833         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
834                 errno = EBADF;
835                 TALLOC_FREE(frame);
836                 return -1;
837         }
838
839         if (!file->file) {
840                 errno = EINVAL;
841                 TALLOC_FREE(frame);
842                 return -1;      /* Can't lseek a dir ... */
843         }
844
845         switch (whence) {
846         case SEEK_SET:
847                 file->offset = offset;
848                 break;
849         case SEEK_CUR:
850                 file->offset += offset;
851                 break;
852         case SEEK_END:
853                 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
854                 if (SMBC_parse_path(frame,
855                                     context,
856                                     file->fname,
857                                     NULL,
858                                     &server,
859                                     &port,
860                                     &share,
861                                     &path,
862                                     &user,
863                                     &password,
864                                     NULL)) {
865                         errno = EINVAL;
866                         TALLOC_FREE(frame);
867                         return -1;
868                 }
869
870                 /*d_printf(">>>lseek: resolving %s\n", path);*/
871                 status = cli_resolve_path(
872                         frame, "", context->internal->auth_info,
873                         file->srv->cli, path, &targetcli, &targetpath);
874                 if (!NT_STATUS_IS_OK(status)) {
875                         d_printf("Could not resolve %s\n", path);
876                         errno = ENOENT;
877                         TALLOC_FREE(frame);
878                         return -1;
879                 }
880
881                 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
882                 if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
883                                              targetcli, file->cli_fd, NULL,
884                                              &size, NULL, NULL, NULL, NULL,
885                                              NULL))) {
886                         off_t b_size = size;
887                         if (!NT_STATUS_IS_OK(cli_getattrE(targetcli, file->cli_fd,
888                                           NULL, &b_size, NULL, NULL, NULL))) {
889                                 errno = EINVAL;
890                                 TALLOC_FREE(frame);
891                                 return -1;
892                         } else
893                                 size = b_size;
894                 }
895                 file->offset = size + offset;
896                 break;
897         default:
898                 errno = EINVAL;
899                 break;
900         }
901
902         TALLOC_FREE(frame);
903         return file->offset;
904 }
905
906
907 /*
908  * Routine to truncate a file given by its file descriptor, to a specified size
909  */
910
911 int
912 SMBC_ftruncate_ctx(SMBCCTX *context,
913                    SMBCFILE *file,
914                    off_t length)
915 {
916         off_t size = length;
917         char *server = NULL;
918         char *share = NULL;
919         char *user = NULL;
920         char *password = NULL;
921         char *path = NULL;
922         char *targetpath = NULL;
923         uint16_t port = 0;
924         struct cli_state *targetcli = NULL;
925         TALLOC_CTX *frame = talloc_stackframe();
926         NTSTATUS status;
927
928         if (!context || !context->internal->initialized) {
929                 errno = EINVAL;
930                 TALLOC_FREE(frame);
931                 return -1;
932         }
933
934         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
935                 errno = EBADF;
936                 TALLOC_FREE(frame);
937                 return -1;
938         }
939
940         if (!file->file) {
941                 errno = EINVAL;
942                 TALLOC_FREE(frame);
943                 return -1;
944         }
945
946         /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
947         if (SMBC_parse_path(frame,
948                             context,
949                             file->fname,
950                             NULL,
951                             &server,
952                             &port,
953                             &share,
954                             &path,
955                             &user,
956                             &password,
957                             NULL)) {
958                 errno = EINVAL;
959                 TALLOC_FREE(frame);
960                 return -1;
961         }
962
963         /*d_printf(">>>fstat: resolving %s\n", path);*/
964         status = cli_resolve_path(frame, "", context->internal->auth_info,
965                                   file->srv->cli, path,
966                                   &targetcli, &targetpath);
967         if (!NT_STATUS_IS_OK(status)) {
968                 d_printf("Could not resolve %s\n", path);
969                 errno = ENOENT;
970                 TALLOC_FREE(frame);
971                 return -1;
972         }
973         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
974
975         if (!NT_STATUS_IS_OK(cli_ftruncate(targetcli, file->cli_fd, (uint64_t)size))) {
976                 errno = EINVAL;
977                 TALLOC_FREE(frame);
978                 return -1;
979         }
980
981         TALLOC_FREE(frame);
982         return 0;
983 }