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