more smbw cleanups.
[ira/wip.git] / source / smbwrapper / smbw.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4    SMB wrapper functions
5    Copyright (C) Andrew Tridgell 1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "wrapper.h"
24
25 pstring smb_cwd;
26
27 static struct smbw_file *smbw_files;
28 static struct smbw_server *smbw_srvs;
29
30 struct bitmap *smbw_file_bmap;
31 extern pstring global_myname;
32 extern int DEBUGLEVEL;
33
34 int smbw_busy=0;
35
36 /***************************************************** 
37 initialise structures
38 *******************************************************/
39 void smbw_init(void)
40 {
41         extern BOOL in_client;
42         static int initialised;
43         static pstring servicesf = CONFIGFILE;
44         extern FILE *dbf;
45         char *p;
46
47         if (initialised) return;
48         initialised = 1;
49
50         smbw_busy++;
51
52         DEBUGLEVEL = 0;
53         setup_logging("smbw",True);
54
55         dbf = stderr;
56
57         if ((p=getenv("SMBW_LOGFILE"))) {
58                 dbf = fopen(p, "a");
59         }
60
61         smbw_file_bmap = bitmap_allocate(SMBW_MAX_OPEN);
62         if (!smbw_file_bmap) {
63                 exit(1);
64         }
65
66         charset_initialise();
67
68         in_client = True;
69
70         load_interfaces();
71
72         if (!lp_load(servicesf,True,False,False)) {
73                 exit(1);
74         }
75
76         get_myname(global_myname,NULL);
77
78         if ((p=getenv("SMBW_DEBUG"))) {
79                 DEBUGLEVEL = atoi(p);
80         }
81
82         if ((p=getenv(SMBW_PWD_ENV))) {
83                 pstrcpy(smb_cwd, p);
84                 DEBUG(4,("Initial cwd from smb_cwd is %s\n", smb_cwd));
85         } else {
86                 sys_getwd(smb_cwd);
87                 DEBUG(4,("Initial cwd from getwd is %s\n", smb_cwd));
88         }
89         smbw_busy--;
90 }
91
92 /***************************************************** 
93 determine if a file descriptor is a smb one
94 *******************************************************/
95 int smbw_fd(int fd)
96 {
97         if (smbw_busy) return 0;
98         return (fd >= SMBW_FD_OFFSET);
99 }
100
101 /***************************************************** 
102 a crude inode number generator
103 *******************************************************/
104 ino_t smbw_inode(const char *name)
105 {
106         return (ino_t)str_checksum(name);
107 }
108
109 /***************************************************** 
110 remove redundent stuff from a filename
111 *******************************************************/
112 void clean_fname(char *name)
113 {
114         char *p, *p2;
115         int l;
116         int modified = 1;
117
118         if (!name) return;
119
120         while (modified) {
121                 modified = 0;
122
123                 DEBUG(5,("cleaning %s\n", name));
124
125                 if ((p=strstr(name,"/./"))) {
126                         modified = 1;
127                         while (*p) {
128                                 p[0] = p[2];
129                                 p++;
130                         }
131                 }
132
133                 if ((p=strstr(name,"//"))) {
134                         modified = 1;
135                         while (*p) {
136                                 p[0] = p[1];
137                                 p++;
138                         }
139                 }
140
141                 if (strcmp(name,"/../")==0) {
142                         modified = 1;
143                         name[1] = 0;
144                 }
145
146                 if ((p=strstr(name,"/../"))) {
147                         modified = 1;
148                         for (p2=(p>name?p-1:p);p2>name;p2--) {
149                                 if (p2[0] == '/') break;
150                         }
151                         while (*p2) {
152                                 p2[0] = p2[3];
153                                 p2++;
154                         }
155                 }
156
157                 if (strcmp(name,"/..")==0) {
158                         modified = 1;
159                         name[1] = 0;
160                 }
161
162                 l = strlen(name);
163                 p = l>=3?(name+l-3):name;
164                 if (strcmp(p,"/..")==0) {
165                         modified = 1;
166                         for (p2=p-1;p2>name;p2--) {
167                                 if (p2[0] == '/') break;
168                         }
169                         if (p2==name) {
170                                 p[0] = '/';
171                                 p[1] = 0;
172                         } else {
173                                 p2[0] = 0;
174                         }
175                 }
176
177                 l = strlen(name);
178                 p = l>=2?(name+l-2):name;
179                 if (strcmp(p,"/.")==0) {
180                         if (p == name) {
181                                 p[1] = 0;
182                         } else {
183                                 p[0] = 0;
184                         }
185                 }
186
187                 if (strncmp(p=name,"./",2) == 0) {      
188                         modified = 1;
189                         do {
190                                 p[0] = p[2];
191                         } while (*p++);
192                 }
193
194                 l = strlen(p=name);
195                 if (l > 1 && p[l-1] == '/') {
196                         modified = 1;
197                         p[l-1] = 0;
198                 }
199         }
200 }
201
202
203 /***************************************************** 
204 parse a smb path into its components. 
205 *******************************************************/
206 char *smbw_parse_path(const char *fname, char *server, char *share, char *path)
207 {
208         static pstring s;
209         char *p, *p2;
210         int len;
211
212         *server = *share = *path = 0;
213
214         if (fname[0] == '/') {
215                 pstrcpy(s, fname);
216         } else {
217                 slprintf(s,sizeof(s)-1, "%s/%s", smb_cwd, fname);
218         }
219         clean_fname(s);
220
221         DEBUG(5,("cleaned %s (fname=%s cwd=%s)\n", 
222                  s, fname, smb_cwd));
223
224         if (strncmp(s,SMBW_PREFIX,strlen(SMBW_PREFIX))) return s;
225
226         p = s + strlen(SMBW_PREFIX);
227         p2 = strchr(p,'/');
228
229         if (p2) {
230                 len = (int)(p2-p);
231         } else {
232                 len = strlen(p);
233         }
234
235         len = MIN(len,sizeof(fstring)-1);
236
237         strncpy(server, p, len);
238         server[len] = 0;                
239
240         p = p2;
241         if (!p) {
242                 fstrcpy(share,"IPC$");
243                 pstrcpy(path,"");
244                 goto ok;
245         }
246
247         p++;
248         p2 = strchr(p,'/');
249
250         if (p2) {
251                 len = (int)(p2-p);
252         } else {
253                 len = strlen(p);
254         }
255
256         len = MIN(len,sizeof(fstring)-1);
257         
258         strncpy(share, p, len);
259         share[len] = 0;
260
261         p = p2;
262         if (!p) {
263                 pstrcpy(path,"\\");
264                 goto ok;
265         }
266
267         pstrcpy(path,p);
268
269         string_sub(path, "/", "\\");
270
271  ok:
272         DEBUG(5,("parsed path name=%s cwd=%s [%s] [%s] [%s]\n", 
273                  fname, smb_cwd,
274                  server, share, path));
275
276         return s;
277 }
278
279 /***************************************************** 
280 determine if a path name (possibly relative) is in the 
281 smb name space
282 *******************************************************/
283 int smbw_path(const char *path)
284 {
285         fstring server, share;
286         pstring s;
287         char *cwd;
288         int l=strlen(SMBW_PREFIX)-1;
289
290         if (path[0] == '/' && strncmp(path,SMBW_PREFIX,l)) {
291                 return 0;
292         }
293
294         if (smbw_busy) return 0;
295
296         smbw_init();
297
298         DEBUG(3,("smbw_path(%s)\n", path));
299
300         cwd = smbw_parse_path(path, server, share, s);
301
302         if (strncmp(cwd,SMBW_PREFIX,l) == 0 &&
303             (cwd[l] == '/' || cwd[l] == 0)) {
304                 return 1;
305         }
306
307         return 0;
308 }
309
310 /***************************************************** 
311 return a unix errno from a SMB error pair
312 *******************************************************/
313 int smbw_errno(struct cli_state *c)
314 {
315         uint8 eclass;
316         uint32 ecode;
317         int ret;
318
319         ret = cli_error(c, &eclass, &ecode);
320
321         if (ret) {
322                 DEBUG(3,("smbw_error %d %d (0x%x)\n", 
323                          (int)eclass, (int)ecode, (int)ecode));
324         }
325         return ret;
326 }
327
328 /***************************************************** 
329 return a connection to a server (existing or new)
330 *******************************************************/
331 struct smbw_server *smbw_server(char *server, char *share)
332 {
333         struct smbw_server *srv=NULL;
334         struct cli_state c;
335         char *username;
336         char *password;
337         char *workgroup;
338         struct nmb_name called, calling;
339
340         ZERO_STRUCT(c);
341
342         username = getenv("SMBW_USER");
343         if (!username) username = getenv("USER");
344         if (!username) username = "guest";
345
346         workgroup = getenv("SMBW_WORKGROUP");
347         if (!workgroup) workgroup = lp_workgroup();
348
349         password = getenv("SMBW_PASSWORD");
350         if (!password) password = "";
351
352         /* try to use an existing connection */
353         for (srv=smbw_srvs;srv;srv=srv->next) {
354                 if (strcmp(server,srv->server_name)==0 &&
355                     strcmp(share,srv->share_name)==0) return srv;
356         }
357
358         if (server[0] == 0) {
359                 errno = EPERM;
360                 return NULL;
361         }
362
363         /* have to open a new connection */
364         if (!cli_initialise(&c) || !cli_connect(&c, server, NULL)) {
365                 errno = ENOENT;
366                 return NULL;
367         }
368
369         make_nmb_name(&calling, global_myname, 0x0, "");
370         make_nmb_name(&called , server, 0x20, "");
371
372         if (!cli_session_request(&c, &calling, &called)) {
373                 cli_shutdown(&c);
374                 errno = ENOENT;
375                 return NULL;
376         }
377
378         if (!cli_negprot(&c)) {
379                 cli_shutdown(&c);
380                 errno = ENOENT;
381                 return NULL;
382         }
383
384         if (!cli_session_setup(&c, username, 
385                                password, strlen(password),
386                                password, strlen(password),
387                                workgroup)) {
388                 cli_shutdown(&c);
389                 errno = EPERM;
390                 return NULL;
391         }
392
393         if (!cli_send_tconX(&c, share, 
394                             strstr(share,"IPC$")?"IPC":"A:", 
395                             password, strlen(password)+1)) {
396                 errno = smbw_errno(&c);
397                 cli_shutdown(&c);
398                 return NULL;
399         }
400
401         srv = (struct smbw_server *)malloc(sizeof(*srv));
402         if (!srv) {
403                 errno = ENOMEM;
404                 goto failed;
405         }
406
407         ZERO_STRUCTP(srv);
408
409         srv->cli = c;
410
411         srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
412
413         srv->server_name = strdup(server);
414         if (!srv->server_name) {
415                 errno = ENOMEM;
416                 goto failed;
417         }
418
419         srv->share_name = strdup(share);
420         if (!srv->share_name) {
421                 errno = ENOMEM;
422                 goto failed;
423         }
424
425         /* some programs play with file descriptors fairly intimately. We
426            try to get out of the way by duping to a high fd number */
427         if (fcntl(SMBW_CLI_FD + srv->cli.fd, F_GETFD) && errno == EBADF) {
428                 if (dup2(srv->cli.fd,SMBW_CLI_FD+srv->cli.fd) == 
429                     srv->cli.fd+SMBW_CLI_FD) {
430                         close(srv->cli.fd);
431                         srv->cli.fd += SMBW_CLI_FD;
432                 }
433         }
434
435         DLIST_ADD(smbw_srvs, srv);
436
437         return srv;
438
439  failed:
440         cli_shutdown(&c);
441         if (!srv) return NULL;
442
443         if (srv->server_name) free(srv->server_name);
444         if (srv->share_name) free(srv->share_name);
445         free(srv);
446         return NULL;
447 }
448
449
450 /***************************************************** 
451 map a fd to a smbw_file structure
452 *******************************************************/
453 struct smbw_file *smbw_file(int fd)
454 {
455         struct smbw_file *file;
456
457         for (file=smbw_files;file;file=file->next) {
458                 if (file->fd == fd) return file;
459         }
460         return NULL;
461 }
462
463 /***************************************************** 
464 a wrapper for open()
465 *******************************************************/
466 int smbw_open(const char *fname, int flags, mode_t mode)
467 {
468         fstring server, share;
469         pstring path;
470         struct smbw_server *srv=NULL;
471         int eno, fd = -1;
472         struct smbw_file *file=NULL;
473
474         DEBUG(4,("%s\n", __FUNCTION__));
475
476         smbw_init();
477
478         if (!fname) {
479                 errno = EINVAL;
480                 return -1;
481         }
482
483         smbw_busy++;    
484
485         /* work out what server they are after */
486         smbw_parse_path(fname, server, share, path);
487
488         /* get a connection to the server */
489         srv = smbw_server(server, share);
490         if (!srv) {
491                 /* smbw_server sets errno */
492                 goto failed;
493         }
494
495         if (path[strlen(path)-1] == '\\') {
496                 fd = -1;
497         } else {
498                 fd = cli_open(&srv->cli, path, flags, DENY_NONE);
499         }
500         if (fd == -1) {
501                 /* it might be a directory. Maybe we should use chkpath? */
502                 fd = smbw_dir_open(fname);
503                 smbw_busy--;
504                 return fd;
505         }
506         if (fd == -1) {
507                 errno = eno;
508                 goto failed;
509         }
510
511         file = (struct smbw_file *)malloc(sizeof(*file));
512         if (!file) {
513                 errno = ENOMEM;
514                 goto failed;
515         }
516
517         ZERO_STRUCTP(file);
518
519         file->cli_fd = fd;
520         file->fname = strdup(path);
521         if (!file->fname) {
522                 errno = ENOMEM;
523                 goto failed;
524         }
525         file->srv = srv;
526         file->fd = bitmap_find(smbw_file_bmap, 0);
527
528         if (file->fd == -1) {
529                 errno = EMFILE;
530                 goto failed;
531         }
532
533         bitmap_set(smbw_file_bmap, file->fd);
534
535         file->fd += SMBW_FD_OFFSET;
536
537         DLIST_ADD(smbw_files, file);
538
539         DEBUG(4,("opened %s\n", fname));
540
541         smbw_busy--;
542         return file->fd;
543
544  failed:
545         if (fd != -1) {
546                 cli_close(&srv->cli, fd);
547         }
548         if (file) {
549                 if (file->fname) {
550                         free(file->fname);
551                 }
552                 free(file);
553         }
554         smbw_busy--;
555         return -1;
556 }
557
558
559 /***************************************************** 
560 a wrapper for read()
561 *******************************************************/
562 ssize_t smbw_read(int fd, void *buf, size_t count)
563 {
564         struct smbw_file *file;
565         int ret;
566
567         DEBUG(4,("%s %d\n", __FUNCTION__, (int)count));
568
569         smbw_busy++;
570
571         file = smbw_file(fd);
572         if (!file) {
573                 errno = EBADF;
574                 smbw_busy--;
575                 return -1;
576         }
577         
578         ret = cli_read(&file->srv->cli, file->cli_fd, buf, file->offset, count);
579
580         if (ret == -1) {
581                 errno = smbw_errno(&file->srv->cli);
582                 smbw_busy--;
583                 return -1;
584         }
585
586         file->offset += ret;
587
588         smbw_busy--;
589         return ret;
590 }
591
592 /***************************************************** 
593 a wrapper for write()
594 *******************************************************/
595 ssize_t smbw_write(int fd, void *buf, size_t count)
596 {
597         struct smbw_file *file;
598         int ret;
599
600         DEBUG(4,("%s\n", __FUNCTION__));
601
602         smbw_busy++;
603
604         file = smbw_file(fd);
605         if (!file) {
606                 DEBUG(3,("bad fd in read\n"));
607                 errno = EBADF;
608                 smbw_busy--;
609                 return -1;
610         }
611         
612         ret = cli_write(&file->srv->cli, file->cli_fd, buf, file->offset, count);
613
614         if (ret == -1) {
615                 errno = smbw_errno(&file->srv->cli);
616                 smbw_busy--;
617                 return -1;
618         }
619
620         file->offset += ret;
621
622         smbw_busy--;
623         return ret;
624 }
625
626 /***************************************************** 
627 a wrapper for close()
628 *******************************************************/
629 int smbw_close(int fd)
630 {
631         struct smbw_file *file;
632
633         DEBUG(4,("%s\n", __FUNCTION__));
634
635         smbw_busy++;
636
637         file = smbw_file(fd);
638         if (!file) {
639                 int ret = smbw_dir_close(fd);
640                 smbw_busy--;
641                 return ret;
642         }
643         
644         if (!cli_close(&file->srv->cli, file->cli_fd)) {
645                 errno = smbw_errno(&file->srv->cli);
646                 smbw_busy--;
647                 return -1;
648         }
649
650
651         bitmap_clear(smbw_file_bmap, file->fd - SMBW_FD_OFFSET);
652         
653         DLIST_REMOVE(smbw_files, file);
654
655         free(file->fname);
656         ZERO_STRUCTP(file);
657         free(file);
658         
659         smbw_busy--;
660
661         return 0;
662 }
663
664
665 /***************************************************** 
666 a wrapper for fcntl()
667 *******************************************************/
668 int smbw_fcntl(int fd, int cmd, long arg)
669 {
670         DEBUG(4,("%s\n", __FUNCTION__));
671         return 0;
672 }
673
674
675 /***************************************************** 
676 a wrapper for access()
677 *******************************************************/
678 int smbw_access(const char *name, int mode)
679 {
680         struct stat st;
681         /* how do we map this properly ?? */
682         return smbw_stat(name, &st);
683 }
684
685 /***************************************************** 
686 a wrapper for realink() - needed for correct errno setting
687 *******************************************************/
688 int smbw_readlink(const char *path, char *buf, size_t bufsize)
689 {
690         struct stat st;
691         int ret;
692
693         ret = smbw_stat(path, &st);
694         if (ret != 0) {
695                 DEBUG(4,("readlink(%s) failed\n", path));
696                 return -1;
697         }
698         
699         /* it exists - say it isn't a link */
700         DEBUG(4,("readlink(%s) not a link\n", path));
701
702         errno = EINVAL;
703         return -1;
704 }
705
706
707 /***************************************************** 
708 a wrapper for unlink()
709 *******************************************************/
710 int smbw_unlink(const char *fname)
711 {
712         struct smbw_server *srv;
713         fstring server, share;
714         pstring path;
715
716         DEBUG(4,("%s (%s)\n", __FUNCTION__, fname));
717
718         if (!fname) {
719                 errno = EINVAL;
720                 return -1;
721         }
722
723         smbw_init();
724
725         smbw_busy++;
726
727         /* work out what server they are after */
728         smbw_parse_path(fname, server, share, path);
729
730         /* get a connection to the server */
731         srv = smbw_server(server, share);
732         if (!srv) {
733                 /* smbw_server sets errno */
734                 goto failed;
735         }
736
737         if (!cli_unlink(&srv->cli, path)) {
738                 errno = smbw_errno(&srv->cli);
739                 goto failed;
740         }
741
742         smbw_busy--;
743         return 0;
744
745  failed:
746         smbw_busy--;
747         return -1;
748 }
749
750
751 /***************************************************** 
752 a wrapper for rename()
753 *******************************************************/
754 int smbw_rename(const char *oldname, const char *newname)
755 {
756         struct smbw_server *srv;
757         fstring server1, share1;
758         pstring path1;
759         fstring server2, share2;
760         pstring path2;
761
762         DEBUG(4,("%s (%s, %s)\n", __FUNCTION__, oldname, newname));
763
764         if (!oldname || !newname) {
765                 errno = EINVAL;
766                 return -1;
767         }
768
769         smbw_init();
770
771         smbw_busy++;
772
773         /* work out what server they are after */
774         smbw_parse_path(oldname, server1, share1, path1);
775         smbw_parse_path(newname, server2, share2, path2);
776
777         if (strcmp(server1, server2) || strcmp(share1, share2)) {
778                 /* can't cross filesystems */
779                 errno = EXDEV;
780                 return -1;
781         }
782
783         /* get a connection to the server */
784         srv = smbw_server(server1, share1);
785         if (!srv) {
786                 /* smbw_server sets errno */
787                 goto failed;
788         }
789
790         if (!cli_rename(&srv->cli, path1, path2)) {
791                 errno = smbw_errno(&srv->cli);
792                 goto failed;
793         }
794
795         smbw_busy--;
796         return 0;
797
798  failed:
799         smbw_busy--;
800         return -1;
801 }
802
803
804 /***************************************************** 
805 a wrapper for utime()
806 *******************************************************/
807 int smbw_utime(const char *fname, void *buf)
808 {
809         struct utimbuf *tbuf = (struct utimbuf *)buf;
810         struct smbw_server *srv;
811         fstring server, share;
812         pstring path;
813         uint32 mode;
814
815         DEBUG(4,("%s (%s)\n", __FUNCTION__, fname));
816
817         if (!fname) {
818                 errno = EINVAL;
819                 return -1;
820         }
821
822         smbw_init();
823
824         smbw_busy++;
825
826         /* work out what server they are after */
827         smbw_parse_path(fname, server, share, path);
828
829         /* get a connection to the server */
830         srv = smbw_server(server, share);
831         if (!srv) {
832                 /* smbw_server sets errno */
833                 goto failed;
834         }
835
836         if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
837                 errno = smbw_errno(&srv->cli);
838                 goto failed;
839         }
840
841         if (!cli_setatr(&srv->cli, path, mode, tbuf->modtime)) {
842                 errno = smbw_errno(&srv->cli);
843                 goto failed;
844         }
845
846         smbw_busy--;
847         return 0;
848
849  failed:
850         smbw_busy--;
851         return -1;
852 }
853
854 /***************************************************** 
855 a wrapper for chown()
856 *******************************************************/
857 int smbw_chown(const char *fname, uid_t owner, gid_t group)
858 {
859         struct smbw_server *srv;
860         fstring server, share;
861         pstring path;
862         uint32 mode;
863
864         DEBUG(4,("%s (%s)\n", __FUNCTION__, fname));
865
866         if (!fname) {
867                 errno = EINVAL;
868                 return -1;
869         }
870
871         smbw_init();
872
873         smbw_busy++;
874
875         /* work out what server they are after */
876         smbw_parse_path(fname, server, share, path);
877
878         /* get a connection to the server */
879         srv = smbw_server(server, share);
880         if (!srv) {
881                 /* smbw_server sets errno */
882                 goto failed;
883         }
884
885         if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
886                 errno = smbw_errno(&srv->cli);
887                 goto failed;
888         }
889         
890         /* assume success */
891
892         smbw_busy--;
893         return 0;
894
895  failed:
896         smbw_busy--;
897         return -1;
898 }
899
900 /***************************************************** 
901 a wrapper for chmod()
902 *******************************************************/
903 int smbw_chmod(const char *fname, mode_t newmode)
904 {
905         struct smbw_server *srv;
906         fstring server, share;
907         pstring path;
908         uint32 mode;
909
910         DEBUG(4,("%s (%s)\n", __FUNCTION__, fname));
911
912         if (!fname) {
913                 errno = EINVAL;
914                 return -1;
915         }
916
917         smbw_init();
918
919         smbw_busy++;
920
921         /* work out what server they are after */
922         smbw_parse_path(fname, server, share, path);
923
924         /* get a connection to the server */
925         srv = smbw_server(server, share);
926         if (!srv) {
927                 /* smbw_server sets errno */
928                 goto failed;
929         }
930
931         if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
932                 errno = smbw_errno(&srv->cli);
933                 goto failed;
934         }
935         
936         /* assume success for the moment - need to add attribute mapping */
937
938         smbw_busy--;
939         return 0;
940
941  failed:
942         smbw_busy--;
943         return -1;
944 }
945
946 /***************************************************** 
947 a wrapper for lseek()
948 *******************************************************/
949 off_t smbw_lseek(int fd, off_t offset, int whence)
950 {
951         struct smbw_file *file;
952         uint32 size;
953
954         DEBUG(4,("%s\n", __FUNCTION__));
955
956         smbw_busy++;
957
958         file = smbw_file(fd);
959         if (!file) {
960                 off_t ret = smbw_dir_lseek(fd, offset, whence);
961                 smbw_busy--;
962                 return ret;
963         }
964
965         switch (whence) {
966         case SEEK_SET:
967                 file->offset = offset;
968                 break;
969         case SEEK_CUR:
970                 file->offset += offset;
971                 break;
972         case SEEK_END:
973                 if (!cli_qfileinfo(&file->srv->cli, file->cli_fd, 
974                                    NULL, &size, NULL, NULL, NULL) &&
975                     !cli_getattrE(&file->srv->cli, file->cli_fd, 
976                                   NULL, &size, NULL, NULL, NULL)) {
977                         errno = EINVAL;
978                         smbw_busy--;
979                         return -1;
980                 }
981                 file->offset = size + offset;
982                 break;
983         }
984
985         smbw_busy--;
986         return file->offset;
987 }
988