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