s3:libsmb: SMBC_getatr do not let ino undefined on success
[mat/samba.git] / source3 / libsmb / libsmb_file.c
1 /*
2    Unix SMB/Netbios implementation.
3    SMB client library implementation
4    Copyright (C) Andrew Tridgell 1998
5    Copyright (C) Richard Sharpe 2000, 2002
6    Copyright (C) John Terpstra 2000
7    Copyright (C) Tom Jansen (Ninja ISD) 2002
8    Copyright (C) Derrell Lipman 2003-2008
9    Copyright (C) Jeremy Allison 2007, 2008
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "libsmb/libsmb.h"
27 #include "libsmbclient.h"
28 #include "libsmb_internal.h"
29 #include "../libcli/smb/smbXcli_base.h"
30
31 /*
32  * Routine to open() a file ...
33  */
34
35 SMBCFILE *
36 SMBC_open_ctx(SMBCCTX *context,
37               const char *fname,
38               int flags,
39               mode_t mode)
40 {
41         char *server = NULL;
42         char *share = NULL;
43         char *user = NULL;
44         char *password = NULL;
45         char *workgroup = NULL;
46         char *path = NULL;
47         char *targetpath = NULL;
48         struct cli_state *targetcli = NULL;
49         SMBCSRV *srv   = NULL;
50         SMBCFILE *file = NULL;
51         uint16_t fd;
52         uint16_t port = 0;
53         NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID;
54         TALLOC_CTX *frame = talloc_stackframe();
55
56         if (!context || !context->internal->initialized) {
57                 errno = EINVAL;  /* Best I can think of ... */
58                 TALLOC_FREE(frame);
59                 return NULL;
60         }
61
62         if (!fname) {
63                 errno = EINVAL;
64                 TALLOC_FREE(frame);
65                 return NULL;
66         }
67
68         if (SMBC_parse_path(frame,
69                             context,
70                             fname,
71                             &workgroup,
72                             &server,
73                             &port,
74                             &share,
75                             &path,
76                             &user,
77                             &password,
78                             NULL)) {
79                 errno = EINVAL;
80                 TALLOC_FREE(frame);
81                 return NULL;
82         }
83
84         if (!user || user[0] == (char)0) {
85                 user = talloc_strdup(frame, smbc_getUser(context));
86                 if (!user) {
87                         errno = ENOMEM;
88                         TALLOC_FREE(frame);
89                         return NULL;
90                 }
91         }
92
93         srv = SMBC_server(frame, context, True,
94                           server, port, share, &workgroup, &user, &password);
95         if (!srv) {
96                 if (errno == EPERM) errno = EACCES;
97                 TALLOC_FREE(frame);
98                 return NULL;  /* SMBC_server sets errno */
99         }
100
101         /* Hmmm, the test for a directory is suspect here ... FIXME */
102
103         if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
104                 status = NT_STATUS_OBJECT_PATH_INVALID;
105         } else {
106                 file = SMB_MALLOC_P(SMBCFILE);
107                 if (!file) {
108                         errno = ENOMEM;
109                         TALLOC_FREE(frame);
110                         return NULL;
111                 }
112
113                 ZERO_STRUCTP(file);
114
115                 /*d_printf(">>>open: resolving %s\n", path);*/
116                 status = cli_resolve_path(
117                         frame, "", context->internal->auth_info,
118                         srv->cli, path, &targetcli, &targetpath);
119                 if (!NT_STATUS_IS_OK(status)) {
120                         d_printf("Could not resolve %s\n", path);
121                         errno = ENOENT;
122                         SAFE_FREE(file);
123                         TALLOC_FREE(frame);
124                         return NULL;
125                 }
126                 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
127
128                 status = cli_open(targetcli, targetpath, flags,
129                                    context->internal->share_mode, &fd);
130                 if (!NT_STATUS_IS_OK(status)) {
131
132                         /* Handle the error ... */
133
134                         SAFE_FREE(file);
135                         errno = SMBC_errno(context, targetcli);
136                         TALLOC_FREE(frame);
137                         return NULL;
138                 }
139
140                 /* Fill in file struct */
141
142                 file->cli_fd  = fd;
143                 file->fname   = SMB_STRDUP(fname);
144                 file->srv     = srv;
145                 file->offset  = 0;
146                 file->file    = True;
147
148                 DLIST_ADD(context->internal->files, file);
149
150                 /*
151                  * If the file was opened in O_APPEND mode, all write
152                  * operations should be appended to the file.  To do that,
153                  * though, using this protocol, would require a getattrE()
154                  * call for each and every write, to determine where the end
155                  * of the file is. (There does not appear to be an append flag
156                  * in the protocol.)  Rather than add all of that overhead of
157                  * retrieving the current end-of-file offset prior to each
158                  * write operation, we'll assume that most append operations
159                  * will continuously write, so we'll just set the offset to
160                  * the end of the file now and hope that's adequate.
161                  *
162                  * Note to self: If this proves inadequate, and O_APPEND
163                  * should, in some cases, be forced for each write, add a
164                  * field in the context options structure, for
165                  * "strict_append_mode" which would select between the current
166                  * behavior (if FALSE) or issuing a getattrE() prior to each
167                  * write and forcing the write to the end of the file (if
168                  * TRUE).  Adding that capability will likely require adding
169                  * an "append" flag into the _SMBCFILE structure to track
170                  * whether a file was opened in O_APPEND mode.  -- djl
171                  */
172                 if (flags & O_APPEND) {
173                         if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
174                                 (void) SMBC_close_ctx(context, file);
175                                 errno = ENXIO;
176                                 TALLOC_FREE(frame);
177                                 return NULL;
178                         }
179                 }
180
181                 TALLOC_FREE(frame);
182                 return file;
183         }
184
185         /* Check if opendir needed ... */
186
187         if (!NT_STATUS_IS_OK(status)) {
188                 int eno = 0;
189
190                 eno = SMBC_errno(context, srv->cli);
191                 file = smbc_getFunctionOpendir(context)(context, fname);
192                 if (!file) errno = eno;
193                 TALLOC_FREE(frame);
194                 return file;
195         }
196
197         errno = EINVAL; /* FIXME, correct errno ? */
198         TALLOC_FREE(frame);
199         return NULL;
200 }
201
202 /*
203  * Routine to create a file
204  */
205
206 SMBCFILE *
207 SMBC_creat_ctx(SMBCCTX *context,
208                const char *path,
209                mode_t mode)
210 {
211         if (!context || !context->internal->initialized) {
212                 errno = EINVAL;
213                 return NULL;
214         }
215
216         return SMBC_open_ctx(context, path,
217                              O_WRONLY | O_CREAT | O_TRUNC, mode);
218 }
219
220 /*
221  * Routine to read() a file ...
222  */
223
224 ssize_t
225 SMBC_read_ctx(SMBCCTX *context,
226               SMBCFILE *file,
227               void *buf,
228               size_t count)
229 {
230         size_t ret;
231         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
232         char *path = NULL;
233         char *targetpath = NULL;
234         struct cli_state *targetcli = NULL;
235         uint16_t port = 0;
236         TALLOC_CTX *frame = talloc_stackframe();
237         NTSTATUS status;
238
239         /*
240          * offset:
241          *
242          * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
243          * appears to pass file->offset (which is type off_t) differently than
244          * a local variable of type off_t.  Using local variable "offset" in
245          * the call to cli_read() instead of file->offset fixes a problem
246          * retrieving data at an offset greater than 4GB.
247          */
248         off_t offset;
249
250         if (!context || !context->internal->initialized) {
251                 errno = EINVAL;
252                 TALLOC_FREE(frame);
253                 return -1;
254         }
255
256         DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
257
258         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
259                 errno = EBADF;
260                 TALLOC_FREE(frame);
261                 return -1;
262         }
263
264         offset = file->offset;
265
266         /* Check that the buffer exists ... */
267
268         if (buf == NULL) {
269                 errno = EINVAL;
270                 TALLOC_FREE(frame);
271                 return -1;
272         }
273
274         /*d_printf(">>>read: parsing %s\n", file->fname);*/
275         if (SMBC_parse_path(frame,
276                             context,
277                             file->fname,
278                             NULL,
279                             &server,
280                             &port,
281                             &share,
282                             &path,
283                             &user,
284                             &password,
285                             NULL)) {
286                 errno = EINVAL;
287                 TALLOC_FREE(frame);
288                 return -1;
289         }
290
291         /*d_printf(">>>read: resolving %s\n", path);*/
292         status = cli_resolve_path(frame, "", context->internal->auth_info,
293                                   file->srv->cli, path,
294                                   &targetcli, &targetpath);
295         if (!NT_STATUS_IS_OK(status)) {
296                 d_printf("Could not resolve %s\n", path);
297                 errno = ENOENT;
298                 TALLOC_FREE(frame);
299                 return -1;
300         }
301         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
302
303         status = cli_read(targetcli, file->cli_fd, (char *)buf, offset,
304                           count, &ret);
305         if (!NT_STATUS_IS_OK(status)) {
306                 errno = SMBC_errno(context, targetcli);
307                 TALLOC_FREE(frame);
308                 return -1;
309         }
310
311         file->offset += ret;
312
313         DEBUG(4, ("  --> %ld\n", (unsigned long)ret));
314
315         TALLOC_FREE(frame);
316         return ret;  /* Success, ret bytes of data ... */
317 }
318
319 /*
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 this is NT then don't bother with the getatr */
564         if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
565                 errno = EPERM;
566                 TALLOC_FREE(frame);
567                 return False;
568         }
569
570         if (NT_STATUS_IS_OK(cli_getatr(targetcli, targetpath, mode, size, &write_time))) {
571                 struct timespec w_time_ts;
572
573                 w_time_ts = convert_time_t_to_timespec(write_time);
574                 if (write_time_ts != NULL) {
575                         *write_time_ts = w_time_ts;
576                 }
577                 if (create_time_ts != NULL) {
578                         *create_time_ts = w_time_ts;
579                 }
580                 if (access_time_ts != NULL) {
581                         *access_time_ts = w_time_ts;
582                 }
583                 if (change_time_ts != NULL) {
584                         *change_time_ts = w_time_ts;
585                 }
586                 if (ino) {
587                         *ino = 0;
588                 }
589                 TALLOC_FREE(frame);
590                 return True;
591         }
592
593         errno = EPERM;
594         TALLOC_FREE(frame);
595         return False;
596 }
597
598 /*
599  * Set file info on an SMB server.  Use setpathinfo call first.  If that
600  * fails, use setattrE..
601  *
602  * Access and modification time parameters are always used and must be
603  * provided.  Create time, if zero, will be determined from the actual create
604  * time of the file.  If non-zero, the create time will be set as well.
605  *
606  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
607  */
608 bool
609 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
610             time_t create_time,
611             time_t access_time,
612             time_t write_time,
613             time_t change_time,
614             uint16 mode)
615 {
616         uint16_t fd;
617         int ret;
618         TALLOC_CTX *frame = talloc_stackframe();
619
620         /*
621          * First, try setpathinfo (if qpathinfo succeeded), for it is the
622          * modern function for "new code" to be using, and it works given a
623          * filename rather than requiring that the file be opened to have its
624          * attributes manipulated.
625          */
626         if (srv->no_pathinfo ||
627             !NT_STATUS_IS_OK(cli_setpathinfo_basic(srv->cli, path,
628                                                    create_time,
629                                                    access_time,
630                                                    write_time,
631                                                    change_time,
632                                                    mode))) {
633
634                 /*
635                  * setpathinfo is not supported; go to plan B.
636                  *
637                  * cli_setatr() does not work on win98, and it also doesn't
638                  * support setting the access time (only the modification
639                  * time), so in all cases, we open the specified file and use
640                  * cli_setattrE() which should work on all OS versions, and
641                  * supports both times.
642                  */
643
644                 /* Don't try {q,set}pathinfo() again, with this server */
645                 srv->no_pathinfo = True;
646
647                 /* Open the file */
648                 if (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) {
649                         errno = SMBC_errno(context, srv->cli);
650                         TALLOC_FREE(frame);
651                         return -1;
652                 }
653
654                 /* Set the new attributes */
655                 ret = NT_STATUS_IS_OK(cli_setattrE(srv->cli, fd,
656                                    change_time,
657                                    access_time,
658                                    write_time));
659
660                 /* Close the file */
661                 cli_close(srv->cli, fd);
662
663                 /*
664                  * Unfortunately, setattrE() doesn't have a provision for
665                  * setting the access mode (attributes).  We'll have to try
666                  * cli_setatr() for that, and with only this parameter, it
667                  * seems to work on win98.
668                  */
669                 if (ret && mode != (uint16) -1) {
670                         ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0));
671                 }
672
673                 if (! ret) {
674                         errno = SMBC_errno(context, srv->cli);
675                         TALLOC_FREE(frame);
676                         return False;
677                 }
678         }
679
680         TALLOC_FREE(frame);
681         return True;
682 }
683
684 /*
685  * A routine to lseek() a file
686  */
687
688 off_t
689 SMBC_lseek_ctx(SMBCCTX *context,
690                SMBCFILE *file,
691                off_t offset,
692                int whence)
693 {
694         off_t size;
695         char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
696         char *path = NULL;
697         char *targetpath = NULL;
698         struct cli_state *targetcli = NULL;
699         uint16_t port = 0;
700         TALLOC_CTX *frame = talloc_stackframe();
701         NTSTATUS status;
702
703         if (!context || !context->internal->initialized) {
704                 errno = EINVAL;
705                 TALLOC_FREE(frame);
706                 return -1;
707         }
708
709         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
710                 errno = EBADF;
711                 TALLOC_FREE(frame);
712                 return -1;
713         }
714
715         if (!file->file) {
716                 errno = EINVAL;
717                 TALLOC_FREE(frame);
718                 return -1;      /* Can't lseek a dir ... */
719         }
720
721         switch (whence) {
722         case SEEK_SET:
723                 file->offset = offset;
724                 break;
725         case SEEK_CUR:
726                 file->offset += offset;
727                 break;
728         case SEEK_END:
729                 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
730                 if (SMBC_parse_path(frame,
731                                     context,
732                                     file->fname,
733                                     NULL,
734                                     &server,
735                                     &port,
736                                     &share,
737                                     &path,
738                                     &user,
739                                     &password,
740                                     NULL)) {
741                         errno = EINVAL;
742                         TALLOC_FREE(frame);
743                         return -1;
744                 }
745
746                 /*d_printf(">>>lseek: resolving %s\n", path);*/
747                 status = cli_resolve_path(
748                         frame, "", context->internal->auth_info,
749                         file->srv->cli, path, &targetcli, &targetpath);
750                 if (!NT_STATUS_IS_OK(status)) {
751                         d_printf("Could not resolve %s\n", path);
752                         errno = ENOENT;
753                         TALLOC_FREE(frame);
754                         return -1;
755                 }
756
757                 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
758                 if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
759                                              targetcli, file->cli_fd, NULL,
760                                              &size, NULL, NULL, NULL, NULL,
761                                              NULL))) {
762                         off_t b_size = size;
763                         if (!NT_STATUS_IS_OK(cli_getattrE(targetcli, file->cli_fd,
764                                           NULL, &b_size, NULL, NULL, NULL))) {
765                                 errno = EINVAL;
766                                 TALLOC_FREE(frame);
767                                 return -1;
768                         } else
769                                 size = b_size;
770                 }
771                 file->offset = size + offset;
772                 break;
773         default:
774                 errno = EINVAL;
775                 break;
776         }
777
778         TALLOC_FREE(frame);
779         return file->offset;
780 }
781
782
783 /*
784  * Routine to truncate a file given by its file descriptor, to a specified size
785  */
786
787 int
788 SMBC_ftruncate_ctx(SMBCCTX *context,
789                    SMBCFILE *file,
790                    off_t length)
791 {
792         off_t size = length;
793         char *server = NULL;
794         char *share = NULL;
795         char *user = NULL;
796         char *password = NULL;
797         char *path = NULL;
798         char *targetpath = NULL;
799         uint16_t port = 0;
800         struct cli_state *targetcli = NULL;
801         TALLOC_CTX *frame = talloc_stackframe();
802         NTSTATUS status;
803
804         if (!context || !context->internal->initialized) {
805                 errno = EINVAL;
806                 TALLOC_FREE(frame);
807                 return -1;
808         }
809
810         if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
811                 errno = EBADF;
812                 TALLOC_FREE(frame);
813                 return -1;
814         }
815
816         if (!file->file) {
817                 errno = EINVAL;
818                 TALLOC_FREE(frame);
819                 return -1;
820         }
821
822         /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
823         if (SMBC_parse_path(frame,
824                             context,
825                             file->fname,
826                             NULL,
827                             &server,
828                             &port,
829                             &share,
830                             &path,
831                             &user,
832                             &password,
833                             NULL)) {
834                 errno = EINVAL;
835                 TALLOC_FREE(frame);
836                 return -1;
837         }
838
839         /*d_printf(">>>fstat: resolving %s\n", path);*/
840         status = cli_resolve_path(frame, "", context->internal->auth_info,
841                                   file->srv->cli, path,
842                                   &targetcli, &targetpath);
843         if (!NT_STATUS_IS_OK(status)) {
844                 d_printf("Could not resolve %s\n", path);
845                 errno = ENOENT;
846                 TALLOC_FREE(frame);
847                 return -1;
848         }
849         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
850
851         if (!NT_STATUS_IS_OK(cli_ftruncate(targetcli, file->cli_fd, (uint64_t)size))) {
852                 errno = EINVAL;
853                 TALLOC_FREE(frame);
854                 return -1;
855         }
856
857         TALLOC_FREE(frame);
858         return 0;
859 }