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