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