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