s3:libsmb: SMBC_getatr() if no method worked, try all methods again on next attempt
[bbaumbach/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                 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 /*
320  * Routine to write() a file ...
321  */
322
323 ssize_t
324 SMBC_write_ctx(SMBCCTX *context,
325                SMBCFILE *file,
326                const void *buf,
327                size_t count)
328 {
329         off_t offset;
330         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
331         char *path = NULL;
332         char *targetpath = NULL;
333         struct cli_state *targetcli = NULL;
334         uint16_t port = 0;
335         TALLOC_CTX *frame = talloc_stackframe();
336         NTSTATUS status;
337
338         /* First check all pointers before dereferencing them */
339
340         if (!context || !context->internal->initialized) {
341                 errno = EINVAL;
342                 TALLOC_FREE(frame);
343                 return -1;
344         }
345
346         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
347                 errno = EBADF;
348                 TALLOC_FREE(frame);
349                 return -1;
350         }
351
352         /* Check that the buffer exists ... */
353
354         if (buf == NULL) {
355                 errno = EINVAL;
356                 TALLOC_FREE(frame);
357                 return -1;
358         }
359
360         offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
361
362         /*d_printf(">>>write: parsing %s\n", file->fname);*/
363         if (SMBC_parse_path(frame,
364                             context,
365                             file->fname,
366                             NULL,
367                             &server,
368                             &port,
369                             &share,
370                             &path,
371                             &user,
372                             &password,
373                             NULL)) {
374                 errno = EINVAL;
375                 TALLOC_FREE(frame);
376                 return -1;
377         }
378
379         /*d_printf(">>>write: resolving %s\n", path);*/
380         status = cli_resolve_path(frame, "", context->internal->auth_info,
381                                   file->srv->cli, path,
382                                   &targetcli, &targetpath);
383         if (!NT_STATUS_IS_OK(status)) {
384                 d_printf("Could not resolve %s\n", path);
385                 errno = ENOENT;
386                 TALLOC_FREE(frame);
387                 return -1;
388         }
389         /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
390
391         status = cli_writeall(targetcli, file->cli_fd,
392                               0, (const uint8_t *)buf, offset, count, NULL);
393         if (!NT_STATUS_IS_OK(status)) {
394                 errno = map_errno_from_nt_status(status);
395                 TALLOC_FREE(frame);
396                 return -1;
397         }
398
399         file->offset += count;
400
401         TALLOC_FREE(frame);
402         return count;  /* Success, 0 bytes of data ... */
403 }
404
405 /*
406  * Routine to close() a file ...
407  */
408
409 int
410 SMBC_close_ctx(SMBCCTX *context,
411                SMBCFILE *file)
412 {
413         SMBCSRV *srv;
414         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
415         char *path = NULL;
416         char *targetpath = NULL;
417         uint16_t port = 0;
418         struct cli_state *targetcli = NULL;
419         TALLOC_CTX *frame = talloc_stackframe();
420         NTSTATUS status;
421
422         if (!context || !context->internal->initialized) {
423                 errno = EINVAL;
424                 TALLOC_FREE(frame);
425                 return -1;
426         }
427
428         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
429                 errno = EBADF;
430                 TALLOC_FREE(frame);
431                 return -1;
432         }
433
434         /* IS a dir ... */
435         if (!file->file) {
436                 TALLOC_FREE(frame);
437                 return smbc_getFunctionClosedir(context)(context, file);
438         }
439
440         /*d_printf(">>>close: parsing %s\n", file->fname);*/
441         if (SMBC_parse_path(frame,
442                             context,
443                             file->fname,
444                             NULL,
445                             &server,
446                             &port,
447                             &share,
448                             &path,
449                             &user,
450                             &password,
451                             NULL)) {
452                 errno = EINVAL;
453                 TALLOC_FREE(frame);
454                 return -1;
455         }
456
457         /*d_printf(">>>close: resolving %s\n", path);*/
458         status = cli_resolve_path(frame, "", context->internal->auth_info,
459                                   file->srv->cli, path,
460                                   &targetcli, &targetpath);
461         if (!NT_STATUS_IS_OK(status)) {
462                 d_printf("Could not resolve %s\n", path);
463                 errno = ENOENT;
464                 TALLOC_FREE(frame);
465                 return -1;
466         }
467         /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
468
469         if (!NT_STATUS_IS_OK(cli_close(targetcli, file->cli_fd))) {
470                 DEBUG(3, ("cli_close failed on %s. purging server.\n",
471                           file->fname));
472                 /* Deallocate slot and remove the server
473                  * from the server cache if unused */
474                 errno = SMBC_errno(context, targetcli);
475                 srv = file->srv;
476                 DLIST_REMOVE(context->internal->files, file);
477                 SAFE_FREE(file->fname);
478                 SAFE_FREE(file);
479                 smbc_getFunctionRemoveUnusedServer(context)(context, srv);
480                 TALLOC_FREE(frame);
481                 return -1;
482         }
483
484         DLIST_REMOVE(context->internal->files, file);
485         SAFE_FREE(file->fname);
486         SAFE_FREE(file);
487         TALLOC_FREE(frame);
488         return 0;
489 }
490
491 /*
492  * Get info from an SMB server on a file. Use a qpathinfo call first
493  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
494  */
495 bool
496 SMBC_getatr(SMBCCTX * context,
497             SMBCSRV *srv,
498             const char *path,
499             uint16 *mode,
500             off_t *size,
501             struct timespec *create_time_ts,
502             struct timespec *access_time_ts,
503             struct timespec *write_time_ts,
504             struct timespec *change_time_ts,
505             SMB_INO_T *ino)
506 {
507         char *fixedpath = NULL;
508         char *targetpath = NULL;
509         struct cli_state *targetcli = NULL;
510         time_t write_time;
511         TALLOC_CTX *frame = talloc_stackframe();
512         NTSTATUS status;
513
514         if (!context || !context->internal->initialized) {
515                 errno = EINVAL;
516                 TALLOC_FREE(frame);
517                 return False;
518         }
519
520         /* path fixup for . and .. */
521         if (strequal(path, ".") || strequal(path, "..")) {
522                 fixedpath = talloc_strdup(frame, "\\");
523                 if (!fixedpath) {
524                         errno = ENOMEM;
525                         TALLOC_FREE(frame);
526                         return False;
527                 }
528         } else {
529                 fixedpath = talloc_strdup(frame, path);
530                 if (!fixedpath) {
531                         errno = ENOMEM;
532                         TALLOC_FREE(frame);
533                         return False;
534                 }
535                 trim_string(fixedpath, NULL, "\\..");
536                 trim_string(fixedpath, NULL, "\\.");
537         }
538         DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
539
540         status = cli_resolve_path(frame, "", context->internal->auth_info,
541                                   srv->cli, fixedpath,
542                                   &targetcli, &targetpath);
543         if (!NT_STATUS_IS_OK(status)) {
544                 d_printf("Couldn't resolve %s\n", path);
545                 errno = ENOENT;
546                 TALLOC_FREE(frame);
547                 return False;
548         }
549
550         if (!srv->no_pathinfo2 &&
551             NT_STATUS_IS_OK(cli_qpathinfo2(targetcli, targetpath,
552                            create_time_ts,
553                            access_time_ts,
554                            write_time_ts,
555                            change_time_ts,
556                            size, mode, ino))) {
557                 TALLOC_FREE(frame);
558                 return True;
559         }
560
561         srv->no_pathinfo2 = True;
562
563         if (!srv->no_pathinfo3 &&
564             NT_STATUS_IS_OK(cli_qpathinfo3(targetcli, targetpath,
565                            create_time_ts,
566                            access_time_ts,
567                            write_time_ts,
568                            change_time_ts,
569                            size, mode, ino))) {
570                 TALLOC_FREE(frame);
571                 return True;
572         }
573
574         srv->no_pathinfo3 = True;
575
576         /* if this is NT then don't bother with the getatr */
577         if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
578                 goto all_failed;
579         }
580
581         if (NT_STATUS_IS_OK(cli_getatr(targetcli, targetpath, mode, size, &write_time))) {
582                 struct timespec w_time_ts;
583
584                 w_time_ts = convert_time_t_to_timespec(write_time);
585                 if (write_time_ts != NULL) {
586                         *write_time_ts = w_time_ts;
587                 }
588                 if (create_time_ts != NULL) {
589                         *create_time_ts = w_time_ts;
590                 }
591                 if (access_time_ts != NULL) {
592                         *access_time_ts = w_time_ts;
593                 }
594                 if (change_time_ts != NULL) {
595                         *change_time_ts = w_time_ts;
596                 }
597                 if (ino) {
598                         *ino = 0;
599                 }
600                 TALLOC_FREE(frame);
601                 return True;
602         }
603
604 all_failed:
605         srv->no_pathinfo2 = False;
606         srv->no_pathinfo3 = False;
607
608         errno = EPERM;
609         TALLOC_FREE(frame);
610         return False;
611 }
612
613 /*
614  * Set file info on an SMB server.  Use setpathinfo call first.  If that
615  * fails, use setattrE..
616  *
617  * Access and modification time parameters are always used and must be
618  * provided.  Create time, if zero, will be determined from the actual create
619  * time of the file.  If non-zero, the create time will be set as well.
620  *
621  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
622  */
623 bool
624 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
625             time_t create_time,
626             time_t access_time,
627             time_t write_time,
628             time_t change_time,
629             uint16 mode)
630 {
631         uint16_t fd;
632         int ret;
633         TALLOC_CTX *frame = talloc_stackframe();
634
635         /*
636          * First, try setpathinfo (if qpathinfo succeeded), for it is the
637          * modern function for "new code" to be using, and it works given a
638          * filename rather than requiring that the file be opened to have its
639          * attributes manipulated.
640          */
641         if (srv->no_pathinfo ||
642             !NT_STATUS_IS_OK(cli_setpathinfo_basic(srv->cli, path,
643                                                    create_time,
644                                                    access_time,
645                                                    write_time,
646                                                    change_time,
647                                                    mode))) {
648
649                 /*
650                  * setpathinfo is not supported; go to plan B.
651                  *
652                  * cli_setatr() does not work on win98, and it also doesn't
653                  * support setting the access time (only the modification
654                  * time), so in all cases, we open the specified file and use
655                  * cli_setattrE() which should work on all OS versions, and
656                  * supports both times.
657                  */
658
659                 /* Don't try {q,set}pathinfo() again, with this server */
660                 srv->no_pathinfo = True;
661
662                 /* Open the file */
663                 if (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) {
664                         errno = SMBC_errno(context, srv->cli);
665                         TALLOC_FREE(frame);
666                         return -1;
667                 }
668
669                 /* Set the new attributes */
670                 ret = NT_STATUS_IS_OK(cli_setattrE(srv->cli, fd,
671                                    change_time,
672                                    access_time,
673                                    write_time));
674
675                 /* Close the file */
676                 cli_close(srv->cli, fd);
677
678                 /*
679                  * Unfortunately, setattrE() doesn't have a provision for
680                  * setting the access mode (attributes).  We'll have to try
681                  * cli_setatr() for that, and with only this parameter, it
682                  * seems to work on win98.
683                  */
684                 if (ret && mode != (uint16) -1) {
685                         ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0));
686                 }
687
688                 if (! ret) {
689                         errno = SMBC_errno(context, srv->cli);
690                         TALLOC_FREE(frame);
691                         return False;
692                 }
693         }
694
695         TALLOC_FREE(frame);
696         return True;
697 }
698
699 /*
700  * A routine to lseek() a file
701  */
702
703 off_t
704 SMBC_lseek_ctx(SMBCCTX *context,
705                SMBCFILE *file,
706                off_t offset,
707                int whence)
708 {
709         off_t size;
710         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
711         char *path = NULL;
712         char *targetpath = NULL;
713         struct cli_state *targetcli = NULL;
714         uint16_t port = 0;
715         TALLOC_CTX *frame = talloc_stackframe();
716         NTSTATUS status;
717
718         if (!context || !context->internal->initialized) {
719                 errno = EINVAL;
720                 TALLOC_FREE(frame);
721                 return -1;
722         }
723
724         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
725                 errno = EBADF;
726                 TALLOC_FREE(frame);
727                 return -1;
728         }
729
730         if (!file->file) {
731                 errno = EINVAL;
732                 TALLOC_FREE(frame);
733                 return -1;      /* Can't lseek a dir ... */
734         }
735
736         switch (whence) {
737         case SEEK_SET:
738                 file->offset = offset;
739                 break;
740         case SEEK_CUR:
741                 file->offset += offset;
742                 break;
743         case SEEK_END:
744                 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
745                 if (SMBC_parse_path(frame,
746                                     context,
747                                     file->fname,
748                                     NULL,
749                                     &server,
750                                     &port,
751                                     &share,
752                                     &path,
753                                     &user,
754                                     &password,
755                                     NULL)) {
756                         errno = EINVAL;
757                         TALLOC_FREE(frame);
758                         return -1;
759                 }
760
761                 /*d_printf(">>>lseek: resolving %s\n", path);*/
762                 status = cli_resolve_path(
763                         frame, "", context->internal->auth_info,
764                         file->srv->cli, path, &targetcli, &targetpath);
765                 if (!NT_STATUS_IS_OK(status)) {
766                         d_printf("Could not resolve %s\n", path);
767                         errno = ENOENT;
768                         TALLOC_FREE(frame);
769                         return -1;
770                 }
771
772                 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
773                 if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
774                                              targetcli, file->cli_fd, NULL,
775                                              &size, NULL, NULL, NULL, NULL,
776                                              NULL))) {
777                         off_t b_size = size;
778                         if (!NT_STATUS_IS_OK(cli_getattrE(targetcli, file->cli_fd,
779                                           NULL, &b_size, NULL, NULL, NULL))) {
780                                 errno = EINVAL;
781                                 TALLOC_FREE(frame);
782                                 return -1;
783                         } else
784                                 size = b_size;
785                 }
786                 file->offset = size + offset;
787                 break;
788         default:
789                 errno = EINVAL;
790                 break;
791         }
792
793         TALLOC_FREE(frame);
794         return file->offset;
795 }
796
797
798 /*
799  * Routine to truncate a file given by its file descriptor, to a specified size
800  */
801
802 int
803 SMBC_ftruncate_ctx(SMBCCTX *context,
804                    SMBCFILE *file,
805                    off_t length)
806 {
807         off_t size = length;
808         char *server = NULL;
809         char *share = NULL;
810         char *user = NULL;
811         char *password = NULL;
812         char *path = NULL;
813         char *targetpath = NULL;
814         uint16_t port = 0;
815         struct cli_state *targetcli = NULL;
816         TALLOC_CTX *frame = talloc_stackframe();
817         NTSTATUS status;
818
819         if (!context || !context->internal->initialized) {
820                 errno = EINVAL;
821                 TALLOC_FREE(frame);
822                 return -1;
823         }
824
825         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
826                 errno = EBADF;
827                 TALLOC_FREE(frame);
828                 return -1;
829         }
830
831         if (!file->file) {
832                 errno = EINVAL;
833                 TALLOC_FREE(frame);
834                 return -1;
835         }
836
837         /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
838         if (SMBC_parse_path(frame,
839                             context,
840                             file->fname,
841                             NULL,
842                             &server,
843                             &port,
844                             &share,
845                             &path,
846                             &user,
847                             &password,
848                             NULL)) {
849                 errno = EINVAL;
850                 TALLOC_FREE(frame);
851                 return -1;
852         }
853
854         /*d_printf(">>>fstat: resolving %s\n", path);*/
855         status = cli_resolve_path(frame, "", context->internal->auth_info,
856                                   file->srv->cli, path,
857                                   &targetcli, &targetpath);
858         if (!NT_STATUS_IS_OK(status)) {
859                 d_printf("Could not resolve %s\n", path);
860                 errno = ENOENT;
861                 TALLOC_FREE(frame);
862                 return -1;
863         }
864         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
865
866         if (!NT_STATUS_IS_OK(cli_ftruncate(targetcli, file->cli_fd, (uint64_t)size))) {
867                 errno = EINVAL;
868                 TALLOC_FREE(frame);
869                 return -1;
870         }
871
872         TALLOC_FREE(frame);
873         return 0;
874 }