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