[Bug 6228] SMBC_open_ctx failure due to path resolve failure doesn't set errno
[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 "libsmbclient.h"
27 #include "libsmb_internal.h"
28
29
30 /*
31  * Routine to open() a file ...
32  */
33
34 SMBCFILE *
35 SMBC_open_ctx(SMBCCTX *context,
36               const char *fname,
37               int flags,
38               mode_t mode)
39 {
40         char *server = NULL;
41         char *share = NULL;
42         char *user = NULL;
43         char *password = NULL;
44         char *workgroup = NULL;
45         char *path = NULL;
46         char *targetpath = NULL;
47         struct cli_state *targetcli = NULL;
48         SMBCSRV *srv   = NULL;
49         SMBCFILE *file = NULL;
50         int fd;
51         TALLOC_CTX *frame = talloc_stackframe();
52         
53         if (!context || !context->internal->initialized) {
54                 
55                 errno = EINVAL;  /* Best I can think of ... */
56                 TALLOC_FREE(frame);
57                 return NULL;
58                 
59         }
60         
61         if (!fname) {
62                 
63                 errno = EINVAL;
64                 TALLOC_FREE(frame);
65                 return NULL;
66                 
67         }
68         
69         if (SMBC_parse_path(frame,
70                             context,
71                             fname,
72                             &workgroup,
73                             &server,
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, share, &workgroup, &user, &password);
95         
96         if (!srv) {
97                 if (errno == EPERM) errno = EACCES;
98                 TALLOC_FREE(frame);
99                 return NULL;  /* SMBC_server sets errno */
100         }
101         
102         /* Hmmm, the test for a directory is suspect here ... FIXME */
103         
104         if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
105                 fd = -1;
106         } else {
107                 file = SMB_MALLOC_P(SMBCFILE);
108                 
109                 if (!file) {
110                         errno = ENOMEM;
111                         TALLOC_FREE(frame);
112                         return NULL;
113                 }
114                 
115                 ZERO_STRUCTP(file);
116                 
117                 /*d_printf(">>>open: resolving %s\n", path);*/
118                 if (!cli_resolve_path(frame, "", context->internal->auth_info,
119                                 srv->cli, path,
120                                 &targetcli, &targetpath)) {
121                         d_printf("Could not resolve %s\n", path);
122                         errno = ENOENT;
123                         SAFE_FREE(file);
124                         TALLOC_FREE(frame);
125                         return NULL;
126                 }
127                 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
128                 
129                 if ((fd = cli_open(targetcli, targetpath, flags,
130                                    context->internal->share_mode)) < 0) {
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                 
141                 /* Fill in file struct */
142                 
143                 file->cli_fd  = fd;
144                 file->fname   = SMB_STRDUP(fname);
145                 file->srv     = srv;
146                 file->offset  = 0;
147                 file->file    = True;
148                 
149                 DLIST_ADD(context->internal->files, file);
150                 
151                 /*
152                  * If the file was opened in O_APPEND mode, all write
153                  * operations should be appended to the file.  To do that,
154                  * though, using this protocol, would require a getattrE()
155                  * call for each and every write, to determine where the end
156                  * of the file is. (There does not appear to be an append flag
157                  * in the protocol.)  Rather than add all of that overhead of
158                  * retrieving the current end-of-file offset prior to each
159                  * write operation, we'll assume that most append operations
160                  * will continuously write, so we'll just set the offset to
161                  * the end of the file now and hope that's adequate.
162                  *
163                  * Note to self: If this proves inadequate, and O_APPEND
164                  * should, in some cases, be forced for each write, add a
165                  * field in the context options structure, for
166                  * "strict_append_mode" which would select between the current
167                  * behavior (if FALSE) or issuing a getattrE() prior to each
168                  * write and forcing the write to the end of the file (if
169                  * TRUE).  Adding that capability will likely require adding
170                  * an "append" flag into the _SMBCFILE structure to track
171                  * whether a file was opened in O_APPEND mode.  -- djl
172                  */
173                 if (flags & O_APPEND) {
174                         if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
175                                 (void) SMBC_close_ctx(context, file);
176                                 errno = ENXIO;
177                                 TALLOC_FREE(frame);
178                                 return NULL;
179                         }
180                 }
181                 
182                 TALLOC_FREE(frame);
183                 return file;
184                 
185         }
186         
187         /* Check if opendir needed ... */
188         
189         if (fd == -1) {
190                 int eno = 0;
191                 
192                 eno = SMBC_errno(context, srv->cli);
193                 file = smbc_getFunctionOpendir(context)(context, fname);
194                 if (!file) errno = eno;
195                 TALLOC_FREE(frame);
196                 return file;
197                 
198         }
199         
200         errno = EINVAL; /* FIXME, correct errno ? */
201         TALLOC_FREE(frame);
202         return NULL;
203         
204 }
205
206 /*
207  * Routine to create a file 
208  */
209
210 SMBCFILE *
211 SMBC_creat_ctx(SMBCCTX *context,
212                const char *path,
213                mode_t mode)
214 {
215         
216         if (!context || !context->internal->initialized) {
217                 
218                 errno = EINVAL;
219                 return NULL;
220                 
221         }
222         
223         return SMBC_open_ctx(context, path,
224                              O_WRONLY | O_CREAT | O_TRUNC, mode);
225 }
226
227 /*
228  * Routine to read() a file ...
229  */
230
231 ssize_t
232 SMBC_read_ctx(SMBCCTX *context,
233               SMBCFILE *file,
234               void *buf,
235               size_t count)
236 {
237         int ret;
238         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
239         char *path = NULL;
240         char *targetpath = NULL;
241         struct cli_state *targetcli = NULL;
242         TALLOC_CTX *frame = talloc_stackframe();
243         
244         /*
245          * offset:
246          *
247          * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
248          * appears to pass file->offset (which is type off_t) differently than
249          * a local variable of type off_t.  Using local variable "offset" in
250          * the call to cli_read() instead of file->offset fixes a problem
251          * retrieving data at an offset greater than 4GB.
252          */
253         off_t offset;
254         
255         if (!context || !context->internal->initialized) {
256                 
257                 errno = EINVAL;
258                 TALLOC_FREE(frame);
259                 return -1;
260                 
261         }
262         
263         DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
264         
265         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
266                 errno = EBADF;
267                 TALLOC_FREE(frame);
268                 return -1;
269                 
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         
283         /*d_printf(">>>read: parsing %s\n", file->fname);*/
284         if (SMBC_parse_path(frame,
285                             context,
286                             file->fname,
287                             NULL,
288                             &server,
289                             &share,
290                             &path,
291                             &user,
292                             &password,
293                             NULL)) {
294                 errno = EINVAL;
295                 TALLOC_FREE(frame);
296                 return -1;
297         }
298         
299         /*d_printf(">>>read: resolving %s\n", path);*/
300         if (!cli_resolve_path(frame, "", context->internal->auth_info,
301                         file->srv->cli, path,
302                         &targetcli, &targetpath)) {
303                 d_printf("Could not resolve %s\n", path);
304                 errno = ENOENT;
305                 TALLOC_FREE(frame);
306                 return -1;
307         }
308         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
309         
310         ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
311         
312         if (ret < 0) {
313                 
314                 errno = SMBC_errno(context, targetcli);
315                 TALLOC_FREE(frame);
316                 return -1;
317                 
318         }
319         
320         file->offset += ret;
321         
322         DEBUG(4, ("  --> %d\n", ret));
323         
324         TALLOC_FREE(frame);
325         return ret;  /* Success, ret bytes of data ... */
326         
327 }
328
329 /*
330  * Routine to write() a file ...
331  */
332
333 ssize_t
334 SMBC_write_ctx(SMBCCTX *context,
335                SMBCFILE *file,
336                const void *buf,
337                size_t count)
338 {
339         int ret;
340         off_t offset;
341         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
342         char *path = NULL;
343         char *targetpath = NULL;
344         struct cli_state *targetcli = NULL;
345         TALLOC_CTX *frame = talloc_stackframe();
346         
347         /* First check all pointers before dereferencing them */
348         
349         if (!context || !context->internal->initialized) {
350                 
351                 errno = EINVAL;
352                 TALLOC_FREE(frame);
353                 return -1;
354                 
355         }
356         
357         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
358                 errno = EBADF;
359                 TALLOC_FREE(frame);
360                 return -1;
361         }
362         
363         /* Check that the buffer exists ... */
364         
365         if (buf == NULL) {
366                 errno = EINVAL;
367                 TALLOC_FREE(frame);
368                 return -1;
369                 
370         }
371         
372         offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
373         
374         /*d_printf(">>>write: parsing %s\n", file->fname);*/
375         if (SMBC_parse_path(frame,
376                             context,
377                             file->fname,
378                             NULL,
379                             &server,
380                             &share,
381                             &path,
382                             &user,
383                             &password,
384                             NULL)) {
385                 errno = EINVAL;
386                 TALLOC_FREE(frame);
387                 return -1;
388         }
389
390         /*d_printf(">>>write: resolving %s\n", path);*/
391         if (!cli_resolve_path(frame, "", context->internal->auth_info,
392                         file->srv->cli, path,
393                         &targetcli, &targetpath)) {
394                 d_printf("Could not resolve %s\n", path);
395                 errno = ENOENT;
396                 TALLOC_FREE(frame);
397                 return -1;
398         }
399         /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
400         
401         ret = cli_write(targetcli, file->cli_fd,
402                         0, (char *)buf, offset, count);
403         
404         if (ret <= 0) {
405                 errno = SMBC_errno(context, targetcli);
406                 TALLOC_FREE(frame);
407                 return -1;
408                 
409         }
410         
411         file->offset += ret;
412         
413         TALLOC_FREE(frame);
414         return ret;  /* Success, 0 bytes of data ... */
415 }
416
417 /*
418  * Routine to close() a file ...
419  */
420
421 int
422 SMBC_close_ctx(SMBCCTX *context,
423                SMBCFILE *file)
424 {
425         SMBCSRV *srv;
426         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
427         char *path = NULL;
428         char *targetpath = NULL;
429         struct cli_state *targetcli = NULL;
430         TALLOC_CTX *frame = talloc_stackframe();
431         
432         if (!context || !context->internal->initialized) {
433                 
434                 errno = EINVAL;
435                 TALLOC_FREE(frame);
436                 return -1;
437         }
438         
439         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
440                 errno = EBADF;
441                 TALLOC_FREE(frame);
442                 return -1;
443         }
444         
445         /* IS a dir ... */
446         if (!file->file) {
447                 TALLOC_FREE(frame);
448                 return smbc_getFunctionClosedir(context)(context, file);
449         }
450         
451         /*d_printf(">>>close: parsing %s\n", file->fname);*/
452         if (SMBC_parse_path(frame,
453                             context,
454                             file->fname,
455                             NULL,
456                             &server,
457                             &share,
458                             &path,
459                             &user,
460                             &password,
461                             NULL)) {
462                 errno = EINVAL;
463                 TALLOC_FREE(frame);
464                 return -1;
465         }
466         
467         /*d_printf(">>>close: resolving %s\n", path);*/
468         if (!cli_resolve_path(frame, "", context->internal->auth_info,
469                         file->srv->cli, path,
470                         &targetcli, &targetpath)) {
471                 d_printf("Could not resolve %s\n", path);
472                 errno = ENOENT;
473                 TALLOC_FREE(frame);
474                 return -1;
475         }
476         /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
477         
478         if (!cli_close(targetcli, file->cli_fd)) {
479                 
480                 DEBUG(3, ("cli_close failed on %s. purging server.\n", 
481                           file->fname));
482                 /* Deallocate slot and remove the server 
483                  * from the server cache if unused */
484                 errno = SMBC_errno(context, targetcli);
485                 srv = file->srv;
486                 DLIST_REMOVE(context->internal->files, file);
487                 SAFE_FREE(file->fname);
488                 SAFE_FREE(file);
489                 smbc_getFunctionRemoveUnusedServer(context)(context, srv);
490                 TALLOC_FREE(frame);
491                 return -1;
492                 
493         }
494         
495         DLIST_REMOVE(context->internal->files, file);
496         SAFE_FREE(file->fname);
497         SAFE_FREE(file);
498         TALLOC_FREE(frame);
499         
500         return 0;
501 }
502
503 /*
504  * Get info from an SMB server on a file. Use a qpathinfo call first
505  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
506  */
507 bool
508 SMBC_getatr(SMBCCTX * context,
509             SMBCSRV *srv,
510             char *path,
511             uint16 *mode,
512             SMB_OFF_T *size,
513             struct timespec *create_time_ts,
514             struct timespec *access_time_ts,
515             struct timespec *write_time_ts,
516             struct timespec *change_time_ts,
517             SMB_INO_T *ino)
518 {
519         char *fixedpath = NULL;
520         char *targetpath = NULL;
521         struct cli_state *targetcli = NULL;
522         time_t write_time;
523         TALLOC_CTX *frame = talloc_stackframe();
524         
525         if (!context || !context->internal->initialized) {
526                 
527                 errno = EINVAL;
528                 TALLOC_FREE(frame);
529                 return False;
530         }
531         
532         /* path fixup for . and .. */
533         if (strequal(path, ".") || strequal(path, "..")) {
534                 fixedpath = talloc_strdup(frame, "\\");
535                 if (!fixedpath) {
536                         errno = ENOMEM;
537                         TALLOC_FREE(frame);
538                         return False;
539                 }
540         } else {
541                 fixedpath = talloc_strdup(frame, path);
542                 if (!fixedpath) {
543                         errno = ENOMEM;
544                         TALLOC_FREE(frame);
545                         return False;
546                 }
547                 trim_string(fixedpath, NULL, "\\..");
548                 trim_string(fixedpath, NULL, "\\.");
549         }
550         DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
551         
552         if (!cli_resolve_path(frame, "", context->internal->auth_info,
553                         srv->cli, fixedpath,
554                         &targetcli, &targetpath)) {
555                 d_printf("Couldn't resolve %s\n", path);
556                 errno = ENOENT;
557                 TALLOC_FREE(frame);
558                 return False;
559         }
560         
561         if (!srv->no_pathinfo2 &&
562             cli_qpathinfo2(targetcli, targetpath,
563                            create_time_ts,
564                            access_time_ts,
565                            write_time_ts,
566                            change_time_ts,
567                            size, mode, ino)) {
568                 TALLOC_FREE(frame);
569                 return True;
570         }
571         
572         /* if this is NT then don't bother with the getatr */
573         if (targetcli->capabilities & CAP_NT_SMBS) {
574                 errno = EPERM;
575                 TALLOC_FREE(frame);
576                 return False;
577         }
578         
579         if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
580                 
581                 struct timespec w_time_ts;
582                 
583                 w_time_ts = convert_time_t_to_timespec(write_time);
584                 
585                 if (write_time_ts != NULL) {
586                         *write_time_ts = w_time_ts;
587                 }
588                 
589                 if (create_time_ts != NULL) {
590                         *create_time_ts = w_time_ts;
591                 }
592                 
593                 if (access_time_ts != NULL) {
594                         *access_time_ts = w_time_ts;
595                 }
596                 
597                 if (change_time_ts != NULL) {
598                         *change_time_ts = w_time_ts;
599                 }
600                 
601                 srv->no_pathinfo2 = True;
602                 TALLOC_FREE(frame);
603                 return True;
604         }
605         
606         errno = EPERM;
607         TALLOC_FREE(frame);
608         return False;
609         
610 }
611
612 /*
613  * Set file info on an SMB server.  Use setpathinfo call first.  If that
614  * fails, use setattrE..
615  *
616  * Access and modification time parameters are always used and must be
617  * provided.  Create time, if zero, will be determined from the actual create
618  * time of the file.  If non-zero, the create time will be set as well.
619  *
620  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
621  */
622 bool
623 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
624             time_t create_time,
625             time_t access_time,
626             time_t write_time,
627             time_t change_time,
628             uint16 mode)
629 {
630         int fd;
631         int ret;
632         TALLOC_CTX *frame = talloc_stackframe();
633         
634         /*
635          * First, try setpathinfo (if qpathinfo succeeded), for it is the
636          * modern function for "new code" to be using, and it works given a
637          * filename rather than requiring that the file be opened to have its
638          * attributes manipulated.
639          */
640         if (srv->no_pathinfo ||
641             ! cli_setpathinfo(srv->cli, path,
642                               create_time,
643                               access_time,
644                               write_time,
645                               change_time,
646                               mode)) {
647                 
648                 /*
649                  * setpathinfo is not supported; go to plan B. 
650                  *
651                  * cli_setatr() does not work on win98, and it also doesn't
652                  * support setting the access time (only the modification
653                  * time), so in all cases, we open the specified file and use
654                  * cli_setattrE() which should work on all OS versions, and
655                  * supports both times.
656                  */
657                 
658                 /* Don't try {q,set}pathinfo() again, with this server */
659                 srv->no_pathinfo = True;
660                 
661                 /* Open the file */
662                 if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
663                         
664                         errno = SMBC_errno(context, srv->cli);
665                         TALLOC_FREE(frame);
666                         return -1;
667                 }
668                 
669                 /* Set the new attributes */
670                 ret = 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 = 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         SMB_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         TALLOC_CTX *frame = talloc_stackframe();
715         
716         if (!context || !context->internal->initialized) {
717                 
718                 errno = EINVAL;
719                 TALLOC_FREE(frame);
720                 return -1;
721         }
722         
723         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
724                 
725                 errno = EBADF;
726                 TALLOC_FREE(frame);
727                 return -1;
728                 
729         }
730         
731         if (!file->file) {
732                 
733                 errno = EINVAL;
734                 TALLOC_FREE(frame);
735                 return -1;      /* Can't lseek a dir ... */
736                 
737         }
738         
739         switch (whence) {
740         case SEEK_SET:
741                 file->offset = offset;
742                 break;
743                 
744         case SEEK_CUR:
745                 file->offset += offset;
746                 break;
747                 
748         case SEEK_END:
749                 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
750                 if (SMBC_parse_path(frame,
751                                     context,
752                                     file->fname,
753                                     NULL,
754                                     &server,
755                                     &share,
756                                     &path,
757                                     &user,
758                                     &password,
759                                     NULL)) {
760                         errno = EINVAL;
761                         TALLOC_FREE(frame);
762                         return -1;
763                 }
764                 
765                 /*d_printf(">>>lseek: resolving %s\n", path);*/
766                 if (!cli_resolve_path(frame, "", context->internal->auth_info,
767                                 file->srv->cli, path,
768                                 &targetcli, &targetpath)) {
769                         d_printf("Could not resolve %s\n", path);
770                         errno = ENOENT;
771                         TALLOC_FREE(frame);
772                         return -1;
773                 }
774                 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
775                 
776                 if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
777                                    &size, NULL, NULL, NULL, NULL, NULL))
778                 {
779                         SMB_OFF_T b_size = size;
780                         if (!cli_getattrE(targetcli, file->cli_fd,
781                                           NULL, &b_size, NULL, NULL, NULL))
782                         {
783                                 errno = EINVAL;
784                                 TALLOC_FREE(frame);
785                                 return -1;
786                         } else
787                                 size = b_size;
788                 }
789                 file->offset = size + offset;
790                 break;
791                 
792         default:
793                 errno = EINVAL;
794                 break;
795                 
796         }
797         
798         TALLOC_FREE(frame);
799         return file->offset;
800         
801 }
802
803
804 /*
805  * Routine to truncate a file given by its file descriptor, to a specified size
806  */
807
808 int
809 SMBC_ftruncate_ctx(SMBCCTX *context,
810                    SMBCFILE *file,
811                    off_t length)
812 {
813         SMB_OFF_T size = length;
814         char *server = NULL;
815         char *share = NULL;
816         char *user = NULL;
817         char *password = NULL;
818         char *path = NULL;
819         char *targetpath = NULL;
820         struct cli_state *targetcli = NULL;
821         TALLOC_CTX *frame = talloc_stackframe();
822         
823         if (!context || !context->internal->initialized) {
824                 
825                 errno = EINVAL;
826                 TALLOC_FREE(frame);
827                 return -1;
828         }
829         
830         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
831                 errno = EBADF;
832                 TALLOC_FREE(frame);
833                 return -1;
834         }
835         
836         if (!file->file) {
837                 errno = EINVAL;
838                 TALLOC_FREE(frame);
839                 return -1;
840         }
841         
842         /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
843         if (SMBC_parse_path(frame,
844                             context,
845                             file->fname,
846                             NULL,
847                             &server,
848                             &share,
849                             &path,
850                             &user,
851                             &password,
852                             NULL)) {
853                 errno = EINVAL;
854                 TALLOC_FREE(frame);
855                 return -1;
856         }
857         
858         /*d_printf(">>>fstat: resolving %s\n", path);*/
859         if (!cli_resolve_path(frame, "", context->internal->auth_info,
860                         file->srv->cli, path,
861                         &targetcli, &targetpath)) {
862                 d_printf("Could not resolve %s\n", path);
863                 errno = ENOENT;
864                 TALLOC_FREE(frame);
865                 return -1;
866         }
867         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
868         
869         if (!cli_ftruncate(targetcli, file->cli_fd, size)) {
870                 errno = EINVAL;
871                 TALLOC_FREE(frame);
872                 return -1;
873         }
874         
875         TALLOC_FREE(frame);
876         return 0;
877         
878 }