added support for browsing the list of workgroups at the top level in
[jra/samba/.git] / source3 / 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 "realcalls.h"
24
25 pstring smbw_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 fstring smbw_prefix = SMBW_PREFIX;
35
36 int smbw_busy=0;
37
38 /* needs to be here because of dumb include files on some systems */
39 int creat_bits = O_WRONLY|O_CREAT|O_TRUNC;
40
41
42 /***************************************************** 
43 initialise structures
44 *******************************************************/
45 void smbw_init(void)
46 {
47         extern BOOL in_client;
48         static int initialised;
49         static pstring servicesf = CONFIGFILE;
50         extern FILE *dbf;
51         char *p;
52         int eno;
53         pstring line;
54
55         if (initialised) return;
56         initialised = 1;
57
58         eno = errno;
59
60         smbw_busy++;
61
62         DEBUGLEVEL = 0;
63         setup_logging("smbsh",True);
64
65         dbf = stderr;
66
67         if ((p=smbw_getshared("LOGFILE"))) {
68                 dbf = sys_fopen(p, "a");
69         }
70
71         smbw_file_bmap = bitmap_allocate(SMBW_MAX_OPEN);
72         if (!smbw_file_bmap) {
73                 exit(1);
74         }
75
76         charset_initialise();
77
78         in_client = True;
79
80         load_interfaces();
81
82         lp_load(servicesf,True,False,False);
83
84         get_myname(global_myname);
85
86         if ((p=smbw_getshared("DEBUG"))) {
87                 DEBUGLEVEL = atoi(p);
88         }
89
90         if ((p=smbw_getshared("RESOLVE_ORDER"))) {
91                 lp_set_name_resolve_order(p);
92         }
93
94         if ((p=smbw_getshared("PREFIX"))) {
95                 slprintf(smbw_prefix,sizeof(fstring)-1, "/%s/", p);
96                 all_string_sub(smbw_prefix,"//", "/", 0);
97                 DEBUG(2,("SMBW_PREFIX is %s\n", smbw_prefix));
98         }
99
100         slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
101         
102         p = smbw_getshared(line);
103         if (!p) {
104                 sys_getwd(smbw_cwd);
105         }
106         pstrcpy(smbw_cwd, p);
107         DEBUG(4,("Initial cwd is %s\n", smbw_cwd));
108
109         smbw_busy--;
110
111         set_maxfiles(SMBW_MAX_OPEN);
112
113         BlockSignals(True,SIGPIPE);
114
115         errno = eno;
116 }
117
118 /***************************************************** 
119 determine if a file descriptor is a smb one
120 *******************************************************/
121 int smbw_fd(int fd)
122 {
123         if (smbw_busy) return 0;
124         return smbw_file_bmap && bitmap_query(smbw_file_bmap, fd);
125 }
126
127 /***************************************************** 
128 determine if a file descriptor is an internal smbw fd
129 *******************************************************/
130 int smbw_local_fd(int fd)
131 {
132         struct smbw_server *srv;
133         
134         smbw_init();
135
136         if (smbw_busy) return 0;
137         if (smbw_shared_fd(fd)) return 1;
138
139         for (srv=smbw_srvs;srv;srv=srv->next) {
140                 if (srv->cli.fd == fd) return 1;
141         }
142
143         return 0;
144 }
145
146 /***************************************************** 
147 a crude inode number generator
148 *******************************************************/
149 ino_t smbw_inode(const char *name)
150 {
151         if (!*name) return 2;
152         return (ino_t)str_checksum(name);
153 }
154
155 /***************************************************** 
156 remove redundent stuff from a filename
157 *******************************************************/
158 void clean_fname(char *name)
159 {
160         char *p, *p2;
161         int l;
162         int modified = 1;
163
164         if (!name) return;
165
166         while (modified) {
167                 modified = 0;
168
169                 DEBUG(5,("cleaning %s\n", name));
170
171                 if ((p=strstr(name,"/./"))) {
172                         modified = 1;
173                         while (*p) {
174                                 p[0] = p[2];
175                                 p++;
176                         }
177                 }
178
179                 if ((p=strstr(name,"//"))) {
180                         modified = 1;
181                         while (*p) {
182                                 p[0] = p[1];
183                                 p++;
184                         }
185                 }
186
187                 if (strcmp(name,"/../")==0) {
188                         modified = 1;
189                         name[1] = 0;
190                 }
191
192                 if ((p=strstr(name,"/../"))) {
193                         modified = 1;
194                         for (p2=(p>name?p-1:p);p2>name;p2--) {
195                                 if (p2[0] == '/') break;
196                         }
197                         while (*p2) {
198                                 p2[0] = p2[3];
199                                 p2++;
200                         }
201                 }
202
203                 if (strcmp(name,"/..")==0) {
204                         modified = 1;
205                         name[1] = 0;
206                 }
207
208                 l = strlen(name);
209                 p = l>=3?(name+l-3):name;
210                 if (strcmp(p,"/..")==0) {
211                         modified = 1;
212                         for (p2=p-1;p2>name;p2--) {
213                                 if (p2[0] == '/') break;
214                         }
215                         if (p2==name) {
216                                 p[0] = '/';
217                                 p[1] = 0;
218                         } else {
219                                 p2[0] = 0;
220                         }
221                 }
222
223                 l = strlen(name);
224                 p = l>=2?(name+l-2):name;
225                 if (strcmp(p,"/.")==0) {
226                         if (p == name) {
227                                 p[1] = 0;
228                         } else {
229                                 p[0] = 0;
230                         }
231                 }
232
233                 if (strncmp(p=name,"./",2) == 0) {      
234                         modified = 1;
235                         do {
236                                 p[0] = p[2];
237                         } while (*p++);
238                 }
239
240                 l = strlen(p=name);
241                 if (l > 1 && p[l-1] == '/') {
242                         modified = 1;
243                         p[l-1] = 0;
244                 }
245         }
246 }
247
248
249
250 /***************************************************** 
251 find a workgroup (any workgroup!) that has a master 
252 browser on the local network
253 *******************************************************/
254 static char *smbw_find_workgroup(void)
255 {
256         fstring server;
257         char *p;
258         struct in_addr *ip_list = NULL;
259         int count = 0;
260         int i;
261
262         /* first off see if an existing workgroup name exists */
263         p = smbw_getshared("WORKGROUP");
264         if (!p) p = lp_workgroup();
265         
266         slprintf(server, sizeof(server), "%s#1D", p);
267         if (smbw_server(server, "IPC$")) return p;
268
269         /* go looking for workgroups */
270         if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
271                 DEBUG(1,("No workgroups found!"));
272                 return p;
273                 
274         }
275
276         free((char *)ip_list);
277
278         DEBUG(0,("Need to do node status code"));
279         return p;
280 }
281
282 /***************************************************** 
283 parse a smb path into its components. 
284 server is one of
285   1) the name of the SMB server
286   2) WORKGROUP#1D for share listing
287   3) WORKGROUP#__ for workgroup listing
288 share is the share on the server to query
289 path is the SMB path on the server
290 return the full path (ie. add cwd if needed)
291 *******************************************************/
292 char *smbw_parse_path(const char *fname, char *server, char *share, char *path)
293 {
294         static pstring s;
295         char *p;
296         int len;
297         fstring workgroup;
298
299         /* add cwd if necessary */
300         if (fname[0] != '/') {
301                 slprintf(s, sizeof(s), "%s/%s", smbw_cwd, fname);
302         } else {
303                 pstrcpy(s, fname);
304         }
305         clean_fname(s);
306
307         /* see if it has the right prefix */
308         len = strlen(smbw_prefix)-1;
309         if (strncmp(s,smbw_prefix,len) || 
310             (s[len] != '/' && s[len] != 0)) return s;
311
312         /* ok, its for us. Now parse out the workgroup, share etc. */
313         p = s+len;
314         if (*p == '/') p++;
315         if (!next_token(&p, workgroup, "/", sizeof(fstring))) {
316                 /* we're in /smb - give a list of workgroups */
317                 slprintf(server,sizeof(fstring), "%s#01", smbw_find_workgroup());
318                 fstrcpy(share,"IPC$");
319                 pstrcpy(path,"");
320                 return s;
321         }
322
323         if (!next_token(&p, server, "/", sizeof(fstring))) {
324                 /* we are in /smb/WORKGROUP */
325                 slprintf(server,sizeof(fstring), "%s#1D", workgroup);
326                 fstrcpy(share,"IPC$");
327                 pstrcpy(path,"");
328         }
329
330         if (!next_token(&p, share, "/", sizeof(fstring))) {
331                 /* we are in /smb/WORKGROUP/SERVER */
332                 fstrcpy(share,"IPC$");
333                 pstrcpy(path,"");
334         }
335
336         pstrcpy(path, p);
337
338         all_string_sub(path, "/", "\\", 0);
339
340         return s;
341 }
342
343 /***************************************************** 
344 determine if a path name (possibly relative) is in the 
345 smb name space
346 *******************************************************/
347 int smbw_path(const char *path)
348 {
349         fstring server, share;
350         pstring s;
351         char *cwd;
352         int len;
353
354         if(!path)
355                 return 0;
356
357         /* this is needed to prevent recursion with the BSD malloc which
358            opens /etc/malloc.conf on the first call */
359         if (strncmp(path,"/etc/", 5) == 0) {
360                 return 0;
361         }
362
363         smbw_init();
364
365         len = strlen(smbw_prefix)-1;
366
367         if (path[0] == '/' && strncmp(path,smbw_prefix,len)) {
368                 return 0;
369         }
370
371         if (smbw_busy) return 0;
372
373         DEBUG(3,("smbw_path(%s)\n", path));
374
375         cwd = smbw_parse_path(path, server, share, s);
376
377         if (strncmp(cwd,smbw_prefix,len) == 0 &&
378             (cwd[len] == '/' || cwd[len] == 0)) {
379                 return 1;
380         }
381
382         return 0;
383 }
384
385 /***************************************************** 
386 return a unix errno from a SMB error pair
387 *******************************************************/
388 int smbw_errno(struct cli_state *c)
389 {
390         uint8 eclass;
391         uint32 ecode;
392         int ret;
393
394         ret = cli_error(c, &eclass, &ecode, NULL);
395
396         if (ret) {
397                 DEBUG(3,("smbw_error %d %d (0x%x) -> %d\n", 
398                          (int)eclass, (int)ecode, (int)ecode, ret));
399         }
400         return ret;
401 }
402
403 /* Return a username and password given a server and share name */
404
405 void get_envvar_auth_data(char *server, char *share, char **workgroup,
406                           char **username, char **password)
407 {
408         /* Fall back to shared memory/environment variables */
409
410         *username = smbw_getshared("USER");
411         if (!*username) *username = getenv("USER");
412         if (!*username) *username = "guest";
413
414         *workgroup = smbw_getshared("WORKGROUP");
415         if (!*workgroup) *workgroup = lp_workgroup();
416
417         *password = smbw_getshared("PASSWORD");
418         if (!*password) *password = "";
419 }
420
421 static smbw_get_auth_data_fn get_auth_data_fn = get_envvar_auth_data;
422
423 /*****************************************************
424 set the get auth data function
425 ******************************************************/
426 void smbw_set_auth_data_fn(smbw_get_auth_data_fn fn)
427 {
428         get_auth_data_fn = fn;
429 }
430
431 /***************************************************** 
432 return a connection to a server (existing or new)
433 *******************************************************/
434 struct smbw_server *smbw_server(char *server, char *share)
435 {
436         struct smbw_server *srv=NULL;
437         struct cli_state c;
438         char *username;
439         char *password;
440         char *workgroup;
441         struct nmb_name called, calling;
442         char *p, *server_n = server;
443         fstring group;
444         pstring ipenv;
445         struct in_addr ip;
446         extern struct in_addr ipzero;
447
448         ip = ipzero;
449         ZERO_STRUCT(c);
450
451         get_auth_data_fn(server, share, &workgroup, &username, &password);
452
453         /* try to use an existing connection */
454         for (srv=smbw_srvs;srv;srv=srv->next) {
455                 if (strcmp(server,srv->server_name)==0 &&
456                     strcmp(share,srv->share_name)==0 &&
457                     strcmp(workgroup,srv->workgroup)==0 &&
458                     strcmp(username, srv->username) == 0) 
459                         return srv;
460         }
461
462         if (server[0] == 0) {
463                 errno = EPERM;
464                 return NULL;
465         }
466
467         make_nmb_name(&calling, global_myname, 0x0);
468         make_nmb_name(&called , server, 0x20);
469
470         DEBUG(4,("server_n=[%s] server=[%s]\n", server_n, server));
471
472         if ((p=strchr(server_n,'#')) && 
473             (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
474                 struct in_addr sip;
475                 pstring s;
476
477                 fstrcpy(group, server_n);
478                 p = strchr(group,'#');
479                 *p = 0;
480                 
481                 /* cache the workgroup master lookup */
482                 slprintf(s,sizeof(s)-1,"MASTER_%s", group);
483                 if (!(server_n = smbw_getshared(s))) {
484                         if (!find_master_ip(group, &sip)) {
485                                 errno = ENOENT;
486                                 return NULL;
487                         }
488                         fstrcpy(group, inet_ntoa(sip));
489                         server_n = group;
490                         smbw_setshared(s,server_n);
491                 }
492         }
493
494         DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
495
496  again:
497         slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
498
499         ip = ipzero;
500         if ((p=smbw_getshared(ipenv))) {
501                 ip = *(interpret_addr2(p));
502         }
503
504         /* have to open a new connection */
505         if (!cli_initialise(&c) || !cli_connect(&c, server_n, &ip)) {
506                 errno = ENOENT;
507                 return NULL;
508         }
509
510         if (!cli_session_request(&c, &calling, &called)) {
511                 cli_shutdown(&c);
512                 if (strcmp(called.name, "*SMBSERVER")) {
513                         make_nmb_name(&called , "*SMBSERVER", 0x20);
514                         goto again;
515                 }
516                 errno = ENOENT;
517                 return NULL;
518         }
519
520         DEBUG(4,(" session request ok\n"));
521
522         if (!cli_negprot(&c)) {
523                 cli_shutdown(&c);
524                 errno = ENOENT;
525                 return NULL;
526         }
527
528         if (!cli_session_setup(&c, username, 
529                                password, strlen(password),
530                                password, strlen(password),
531                                workgroup) &&
532             /* try an anonymous login if it failed */
533             !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
534                 cli_shutdown(&c);
535                 errno = EPERM;
536                 return NULL;
537         }
538
539         DEBUG(4,(" session setup ok\n"));
540
541         if (!cli_send_tconX(&c, share, "?????",
542                             password, strlen(password)+1)) {
543                 errno = smbw_errno(&c);
544                 cli_shutdown(&c);
545                 return NULL;
546         }
547
548         smbw_setshared(ipenv,inet_ntoa(ip));
549         
550         DEBUG(4,(" tconx ok\n"));
551
552         srv = (struct smbw_server *)malloc(sizeof(*srv));
553         if (!srv) {
554                 errno = ENOMEM;
555                 goto failed;
556         }
557
558         ZERO_STRUCTP(srv);
559
560         srv->cli = c;
561
562         srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
563
564         srv->server_name = strdup(server);
565         if (!srv->server_name) {
566                 errno = ENOMEM;
567                 goto failed;
568         }
569
570         srv->share_name = strdup(share);
571         if (!srv->share_name) {
572                 errno = ENOMEM;
573                 goto failed;
574         }
575
576         srv->workgroup = strdup(workgroup);
577         if (!srv->workgroup) {
578                 errno = ENOMEM;
579                 goto failed;
580         }
581
582         srv->username = strdup(username);
583         if (!srv->username) {
584                 errno = ENOMEM;
585                 goto failed;
586         }
587
588         /* some programs play with file descriptors fairly intimately. We
589            try to get out of the way by duping to a high fd number */
590         if (fcntl(SMBW_CLI_FD + srv->cli.fd, F_GETFD) && errno == EBADF) {
591                 if (dup2(srv->cli.fd,SMBW_CLI_FD+srv->cli.fd) == 
592                     srv->cli.fd+SMBW_CLI_FD) {
593                         close(srv->cli.fd);
594                         srv->cli.fd += SMBW_CLI_FD;
595                 }
596         }
597
598         DLIST_ADD(smbw_srvs, srv);
599
600         return srv;
601
602  failed:
603         cli_shutdown(&c);
604         if (!srv) return NULL;
605
606         if (srv->server_name) free(srv->server_name);
607         if (srv->share_name) free(srv->share_name);
608         free(srv);
609         return NULL;
610 }
611
612
613 /***************************************************** 
614 map a fd to a smbw_file structure
615 *******************************************************/
616 struct smbw_file *smbw_file(int fd)
617 {
618         struct smbw_file *file;
619
620         for (file=smbw_files;file;file=file->next) {
621                 if (file->fd == fd) return file;
622         }
623         return NULL;
624 }
625
626 /***************************************************** 
627 a wrapper for open()
628 *******************************************************/
629 int smbw_open(const char *fname, int flags, mode_t mode)
630 {
631         fstring server, share;
632         pstring path;
633         struct smbw_server *srv=NULL;
634         int eno=0, fd = -1;
635         struct smbw_file *file=NULL;
636
637         smbw_init();
638
639         if (!fname) {
640                 errno = EINVAL;
641                 return -1;
642         }
643
644         smbw_busy++;    
645
646         /* work out what server they are after */
647         smbw_parse_path(fname, server, share, path);
648
649         /* get a connection to the server */
650         srv = smbw_server(server, share);
651         if (!srv) {
652                 /* smbw_server sets errno */
653                 goto failed;
654         }
655
656         if (path[strlen(path)-1] == '\\') {
657                 fd = -1;
658         } else {
659                 fd = cli_open(&srv->cli, path, flags, DENY_NONE);
660         }
661         if (fd == -1) {
662                 /* it might be a directory. Maybe we should use chkpath? */
663                 eno = smbw_errno(&srv->cli);
664                 fd = smbw_dir_open(fname);
665                 if (fd == -1) errno = eno;
666                 smbw_busy--;
667                 return fd;
668         }
669
670         file = (struct smbw_file *)malloc(sizeof(*file));
671         if (!file) {
672                 errno = ENOMEM;
673                 goto failed;
674         }
675
676         ZERO_STRUCTP(file);
677
678         file->f = (struct smbw_filedes *)malloc(sizeof(*(file->f)));
679         if (!file->f) {
680                 errno = ENOMEM;
681                 goto failed;
682         }
683
684         ZERO_STRUCTP(file->f);
685
686         file->f->cli_fd = fd;
687         file->f->fname = strdup(path);
688         if (!file->f->fname) {
689                 errno = ENOMEM;
690                 goto failed;
691         }
692         file->srv = srv;
693         file->fd = open(SMBW_DUMMY, O_WRONLY);
694         if (file->fd == -1) {
695                 errno = EMFILE;
696                 goto failed;
697         }
698
699         if (bitmap_query(smbw_file_bmap, file->fd)) {
700                 DEBUG(0,("ERROR: fd used in smbw_open\n"));
701                 errno = EIO;
702                 goto failed;
703         }
704
705         file->f->ref_count=1;
706
707         bitmap_set(smbw_file_bmap, file->fd);
708
709         DLIST_ADD(smbw_files, file);
710
711         DEBUG(4,("opened %s\n", fname));
712
713         smbw_busy--;
714         return file->fd;
715
716  failed:
717         if (fd != -1) {
718                 cli_close(&srv->cli, fd);
719         }
720         if (file) {
721                 if (file->f) {
722                         if (file->f->fname) {
723                                 free(file->f->fname);
724                         }
725                         free(file->f);
726                 }
727                 free(file);
728         }
729         smbw_busy--;
730         return -1;
731 }
732
733
734 /***************************************************** 
735 a wrapper for pread()
736 *******************************************************/
737 ssize_t smbw_pread(int fd, void *buf, size_t count, off_t ofs)
738 {
739         struct smbw_file *file;
740         int ret;
741
742         smbw_busy++;
743
744         file = smbw_file(fd);
745         if (!file) {
746                 errno = EBADF;
747                 smbw_busy--;
748                 return -1;
749         }
750         
751         ret = cli_read(&file->srv->cli, file->f->cli_fd, buf, ofs, count);
752
753         if (ret == -1) {
754                 errno = smbw_errno(&file->srv->cli);
755                 smbw_busy--;
756                 return -1;
757         }
758
759         smbw_busy--;
760         return ret;
761 }
762
763 /***************************************************** 
764 a wrapper for read()
765 *******************************************************/
766 ssize_t smbw_read(int fd, void *buf, size_t count)
767 {
768         struct smbw_file *file;
769         int ret;
770
771         DEBUG(4,("smbw_read(%d, %d)\n", fd, (int)count));
772
773         smbw_busy++;
774
775         file = smbw_file(fd);
776         if (!file) {
777                 errno = EBADF;
778                 smbw_busy--;
779                 return -1;
780         }
781         
782         ret = cli_read(&file->srv->cli, file->f->cli_fd, buf, 
783                        file->f->offset, count);
784
785         if (ret == -1) {
786                 errno = smbw_errno(&file->srv->cli);
787                 smbw_busy--;
788                 return -1;
789         }
790
791         file->f->offset += ret;
792         
793         DEBUG(4,(" -> %d\n", ret));
794
795         smbw_busy--;
796         return ret;
797 }
798
799         
800
801 /***************************************************** 
802 a wrapper for write()
803 *******************************************************/
804 ssize_t smbw_write(int fd, void *buf, size_t count)
805 {
806         struct smbw_file *file;
807         int ret;
808
809         smbw_busy++;
810
811         file = smbw_file(fd);
812         if (!file) {
813                 errno = EBADF;
814                 smbw_busy--;
815                 return -1;
816         }
817         
818         ret = cli_write(&file->srv->cli, file->f->cli_fd, 0, buf, file->f->offset, count);
819
820         if (ret == -1) {
821                 errno = smbw_errno(&file->srv->cli);
822                 smbw_busy--;
823                 return -1;
824         }
825
826         file->f->offset += ret;
827
828         smbw_busy--;
829         return ret;
830 }
831
832 /***************************************************** 
833 a wrapper for pwrite()
834 *******************************************************/
835 ssize_t smbw_pwrite(int fd, void *buf, size_t count, off_t ofs)
836 {
837         struct smbw_file *file;
838         int ret;
839
840         smbw_busy++;
841
842         file = smbw_file(fd);
843         if (!file) {
844                 errno = EBADF;
845                 smbw_busy--;
846                 return -1;
847         }
848         
849         ret = cli_write(&file->srv->cli, file->f->cli_fd, 0, buf, ofs, count);
850
851         if (ret == -1) {
852                 errno = smbw_errno(&file->srv->cli);
853                 smbw_busy--;
854                 return -1;
855         }
856
857         smbw_busy--;
858         return ret;
859 }
860
861 /***************************************************** 
862 a wrapper for close()
863 *******************************************************/
864 int smbw_close(int fd)
865 {
866         struct smbw_file *file;
867
868         smbw_busy++;
869
870         file = smbw_file(fd);
871         if (!file) {
872                 int ret = smbw_dir_close(fd);
873                 smbw_busy--;
874                 return ret;
875         }
876         
877         if (file->f->ref_count == 1 &&
878             !cli_close(&file->srv->cli, file->f->cli_fd)) {
879                 errno = smbw_errno(&file->srv->cli);
880                 smbw_busy--;
881                 return -1;
882         }
883
884
885         bitmap_clear(smbw_file_bmap, file->fd);
886         close(file->fd);
887         
888         DLIST_REMOVE(smbw_files, file);
889
890         file->f->ref_count--;
891         if (file->f->ref_count == 0) {
892                 free(file->f->fname);
893                 free(file->f);
894         }
895         ZERO_STRUCTP(file);
896         free(file);
897         
898         smbw_busy--;
899
900         return 0;
901 }
902
903
904 /***************************************************** 
905 a wrapper for fcntl()
906 *******************************************************/
907 int smbw_fcntl(int fd, int cmd, long arg)
908 {
909         return 0;
910 }
911
912
913 /***************************************************** 
914 a wrapper for access()
915 *******************************************************/
916 int smbw_access(const char *name, int mode)
917 {
918         struct stat st;
919
920         DEBUG(4,("smbw_access(%s, 0x%x)\n", name, mode));
921
922         if (smbw_stat(name, &st)) return -1;
923
924         if (((mode & R_OK) && !(st.st_mode & S_IRUSR)) ||
925             ((mode & W_OK) && !(st.st_mode & S_IWUSR)) ||
926             ((mode & X_OK) && !(st.st_mode & S_IXUSR))) {
927                 errno = EACCES;
928                 return -1;
929         }
930         
931         return 0;
932 }
933
934 /***************************************************** 
935 a wrapper for realink() - needed for correct errno setting
936 *******************************************************/
937 int smbw_readlink(const char *path, char *buf, size_t bufsize)
938 {
939         struct stat st;
940         int ret;
941
942         ret = smbw_stat(path, &st);
943         if (ret != 0) {
944                 DEBUG(4,("readlink(%s) failed\n", path));
945                 return -1;
946         }
947         
948         /* it exists - say it isn't a link */
949         DEBUG(4,("readlink(%s) not a link\n", path));
950
951         errno = EINVAL;
952         return -1;
953 }
954
955
956 /***************************************************** 
957 a wrapper for unlink()
958 *******************************************************/
959 int smbw_unlink(const char *fname)
960 {
961         struct smbw_server *srv;
962         fstring server, share;
963         pstring path;
964
965         if (!fname) {
966                 errno = EINVAL;
967                 return -1;
968         }
969
970         smbw_init();
971
972         smbw_busy++;
973
974         /* work out what server they are after */
975         smbw_parse_path(fname, server, share, path);
976
977         /* get a connection to the server */
978         srv = smbw_server(server, share);
979         if (!srv) {
980                 /* smbw_server sets errno */
981                 goto failed;
982         }
983
984         if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
985                 int job = smbw_stat_printjob(srv, path, NULL, NULL);
986                 if (job == -1) {
987                         goto failed;
988                 }
989                 if (cli_printjob_del(&srv->cli, job) != 0) {
990                         goto failed;
991                 }
992         } else if (!cli_unlink(&srv->cli, path)) {
993                 errno = smbw_errno(&srv->cli);
994                 goto failed;
995         }
996
997         smbw_busy--;
998         return 0;
999
1000  failed:
1001         smbw_busy--;
1002         return -1;
1003 }
1004
1005
1006 /***************************************************** 
1007 a wrapper for rename()
1008 *******************************************************/
1009 int smbw_rename(const char *oldname, const char *newname)
1010 {
1011         struct smbw_server *srv;
1012         fstring server1, share1;
1013         pstring path1;
1014         fstring server2, share2;
1015         pstring path2;
1016
1017         if (!oldname || !newname) {
1018                 errno = EINVAL;
1019                 return -1;
1020         }
1021
1022         smbw_init();
1023
1024         DEBUG(4,("smbw_rename(%s,%s)\n", oldname, newname));
1025
1026         smbw_busy++;
1027
1028         /* work out what server they are after */
1029         smbw_parse_path(oldname, server1, share1, path1);
1030         smbw_parse_path(newname, server2, share2, path2);
1031
1032         if (strcmp(server1, server2) || strcmp(share1, share2)) {
1033                 /* can't cross filesystems */
1034                 errno = EXDEV;
1035                 return -1;
1036         }
1037
1038         /* get a connection to the server */
1039         srv = smbw_server(server1, share1);
1040         if (!srv) {
1041                 /* smbw_server sets errno */
1042                 goto failed;
1043         }
1044
1045         if (!cli_rename(&srv->cli, path1, path2)) {
1046                 int eno = smbw_errno(&srv->cli);
1047                 if (eno != EEXIST ||
1048                     !cli_unlink(&srv->cli, path2) ||
1049                     !cli_rename(&srv->cli, path1, path2)) {
1050                         errno = eno;
1051                         goto failed;
1052                 }
1053         }
1054
1055         smbw_busy--;
1056         return 0;
1057
1058  failed:
1059         smbw_busy--;
1060         return -1;
1061 }
1062
1063
1064 /***************************************************** 
1065 a wrapper for utime and utimes
1066 *******************************************************/
1067 static int smbw_settime(const char *fname, time_t t)
1068 {
1069         struct smbw_server *srv;
1070         fstring server, share;
1071         pstring path;
1072         uint16 mode;
1073
1074         if (!fname) {
1075                 errno = EINVAL;
1076                 return -1;
1077         }
1078
1079         smbw_init();
1080
1081         smbw_busy++;
1082
1083         /* work out what server they are after */
1084         smbw_parse_path(fname, server, share, path);
1085
1086         /* get a connection to the server */
1087         srv = smbw_server(server, share);
1088         if (!srv) {
1089                 /* smbw_server sets errno */
1090                 goto failed;
1091         }
1092
1093         if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
1094                 errno = smbw_errno(&srv->cli);
1095                 goto failed;
1096         }
1097
1098         if (!cli_setatr(&srv->cli, path, mode, t)) {
1099                 /* some servers always refuse directory changes */
1100                 if (!(mode & aDIR)) {
1101                         errno = smbw_errno(&srv->cli);
1102                         goto failed;
1103                 }
1104         }
1105
1106         smbw_busy--;
1107         return 0;
1108
1109  failed:
1110         smbw_busy--;
1111         return -1;
1112 }
1113
1114 /***************************************************** 
1115 a wrapper for utime 
1116 *******************************************************/
1117 int smbw_utime(const char *fname, void *buf)
1118 {
1119         struct utimbuf *tbuf = (struct utimbuf *)buf;
1120         return smbw_settime(fname, tbuf?tbuf->modtime:time(NULL));
1121 }
1122
1123 /***************************************************** 
1124 a wrapper for utime 
1125 *******************************************************/
1126 int smbw_utimes(const char *fname, void *buf)
1127 {
1128         struct timeval *tbuf = (struct timeval *)buf;
1129         return smbw_settime(fname, tbuf?tbuf->tv_sec:time(NULL));
1130 }
1131
1132
1133 /***************************************************** 
1134 a wrapper for chown()
1135 *******************************************************/
1136 int smbw_chown(const char *fname, uid_t owner, gid_t group)
1137 {
1138         struct smbw_server *srv;
1139         fstring server, share;
1140         pstring path;
1141         uint16 mode;
1142
1143         if (!fname) {
1144                 errno = EINVAL;
1145                 return -1;
1146         }
1147
1148         smbw_init();
1149
1150         smbw_busy++;
1151
1152         /* work out what server they are after */
1153         smbw_parse_path(fname, server, share, path);
1154
1155         /* get a connection to the server */
1156         srv = smbw_server(server, share);
1157         if (!srv) {
1158                 /* smbw_server sets errno */
1159                 goto failed;
1160         }
1161
1162         if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
1163                 errno = smbw_errno(&srv->cli);
1164                 goto failed;
1165         }
1166         
1167         /* assume success */
1168
1169         smbw_busy--;
1170         return 0;
1171
1172  failed:
1173         smbw_busy--;
1174         return -1;
1175 }
1176
1177 /***************************************************** 
1178 a wrapper for chmod()
1179 *******************************************************/
1180 int smbw_chmod(const char *fname, mode_t newmode)
1181 {
1182         struct smbw_server *srv;
1183         fstring server, share;
1184         pstring path;
1185         uint32 mode;
1186
1187         if (!fname) {
1188                 errno = EINVAL;
1189                 return -1;
1190         }
1191
1192         smbw_init();
1193
1194         smbw_busy++;
1195
1196         /* work out what server they are after */
1197         smbw_parse_path(fname, server, share, path);
1198
1199         /* get a connection to the server */
1200         srv = smbw_server(server, share);
1201         if (!srv) {
1202                 /* smbw_server sets errno */
1203                 goto failed;
1204         }
1205
1206         mode = 0;
1207
1208         if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
1209         if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
1210         if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
1211         if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
1212
1213         if (!cli_setatr(&srv->cli, path, mode, 0)) {
1214                 errno = smbw_errno(&srv->cli);
1215                 goto failed;
1216         }
1217         
1218         smbw_busy--;
1219         return 0;
1220
1221  failed:
1222         smbw_busy--;
1223         return -1;
1224 }
1225
1226 /***************************************************** 
1227 a wrapper for lseek()
1228 *******************************************************/
1229 off_t smbw_lseek(int fd, off_t offset, int whence)
1230 {
1231         struct smbw_file *file;
1232         size_t size;
1233
1234         smbw_busy++;
1235
1236         file = smbw_file(fd);
1237         if (!file) {
1238                 off_t ret = smbw_dir_lseek(fd, offset, whence);
1239                 smbw_busy--;
1240                 return ret;
1241         }
1242
1243         switch (whence) {
1244         case SEEK_SET:
1245                 file->f->offset = offset;
1246                 break;
1247         case SEEK_CUR:
1248                 file->f->offset += offset;
1249                 break;
1250         case SEEK_END:
1251                 if (!cli_qfileinfo(&file->srv->cli, file->f->cli_fd, 
1252                                    NULL, &size, NULL, NULL, NULL, 
1253                                    NULL, NULL) &&
1254                     !cli_getattrE(&file->srv->cli, file->f->cli_fd, 
1255                                   NULL, &size, NULL, NULL, NULL)) {
1256                         errno = EINVAL;
1257                         smbw_busy--;
1258                         return -1;
1259                 }
1260                 file->f->offset = size + offset;
1261                 break;
1262         }
1263
1264         smbw_busy--;
1265         return file->f->offset;
1266 }
1267
1268
1269 /***************************************************** 
1270 a wrapper for dup()
1271 *******************************************************/
1272 int smbw_dup(int fd)
1273 {
1274         int fd2;
1275         struct smbw_file *file, *file2;
1276
1277         smbw_busy++;
1278
1279         file = smbw_file(fd);
1280         if (!file) {
1281                 errno = EBADF;
1282                 goto failed;
1283         }
1284
1285         fd2 = dup(file->fd);
1286         if (fd2 == -1) {
1287                 goto failed;
1288         }
1289
1290         if (bitmap_query(smbw_file_bmap, fd2)) {
1291                 DEBUG(0,("ERROR: fd already open in dup!\n"));
1292                 errno = EIO;
1293                 goto failed;
1294         }
1295
1296         file2 = (struct smbw_file *)malloc(sizeof(*file2));
1297         if (!file2) {
1298                 close(fd2);
1299                 errno = ENOMEM;
1300                 goto failed;
1301         }
1302
1303         ZERO_STRUCTP(file2);
1304
1305         *file2 = *file;
1306         file2->fd = fd2;
1307
1308         file->f->ref_count++;
1309
1310         bitmap_set(smbw_file_bmap, fd2);
1311         
1312         DLIST_ADD(smbw_files, file2);
1313         
1314         smbw_busy--;
1315         return fd2;
1316
1317  failed:
1318         smbw_busy--;
1319         return -1;
1320 }
1321
1322
1323 /***************************************************** 
1324 a wrapper for dup2()
1325 *******************************************************/
1326 int smbw_dup2(int fd, int fd2)
1327 {
1328         struct smbw_file *file, *file2;
1329
1330         smbw_busy++;
1331
1332         file = smbw_file(fd);
1333         if (!file) {
1334                 errno = EBADF;
1335                 goto failed;
1336         }
1337
1338         if (bitmap_query(smbw_file_bmap, fd2)) {
1339                 DEBUG(0,("ERROR: fd already open in dup2!\n"));
1340                 errno = EIO;
1341                 goto failed;
1342         }
1343
1344         if (dup2(file->fd, fd2) != fd2) {
1345                 goto failed;
1346         }
1347
1348         file2 = (struct smbw_file *)malloc(sizeof(*file2));
1349         if (!file2) {
1350                 close(fd2);
1351                 errno = ENOMEM;
1352                 goto failed;
1353         }
1354
1355         ZERO_STRUCTP(file2);
1356
1357         *file2 = *file;
1358         file2->fd = fd2;
1359
1360         file->f->ref_count++;
1361
1362         bitmap_set(smbw_file_bmap, fd2);
1363         
1364         DLIST_ADD(smbw_files, file2);
1365         
1366         smbw_busy--;
1367         return fd2;
1368
1369  failed:
1370         smbw_busy--;
1371         return -1;
1372 }
1373
1374
1375 /***************************************************** 
1376 close a connection to a server
1377 *******************************************************/
1378 static void smbw_srv_close(struct smbw_server *srv)
1379 {
1380         smbw_busy++;
1381
1382         cli_shutdown(&srv->cli);
1383
1384         free(srv->server_name);
1385         free(srv->share_name);
1386
1387         DLIST_REMOVE(smbw_srvs, srv);
1388
1389         ZERO_STRUCTP(srv);
1390
1391         free(srv);
1392         
1393         smbw_busy--;
1394 }
1395
1396 /***************************************************** 
1397 when we fork we have to close all connections and files
1398 in the child
1399 *******************************************************/
1400 int smbw_fork(void)
1401 {
1402         pid_t child;
1403         int p[2];
1404         char c=0;
1405         pstring line;
1406
1407         struct smbw_file *file, *next_file;
1408         struct smbw_server *srv, *next_srv;
1409
1410         if (pipe(p)) return real_fork();
1411
1412         child = real_fork();
1413
1414         if (child) {
1415                 /* block the parent for a moment until the sockets are
1416                    closed */
1417                 close(p[1]);
1418                 read(p[0], &c, 1);
1419                 close(p[0]);
1420                 return child;
1421         }
1422
1423         close(p[0]);
1424
1425         /* close all files */
1426         for (file=smbw_files;file;file=next_file) {
1427                 next_file = file->next;
1428                 close(file->fd);
1429         }
1430
1431         /* close all server connections */
1432         for (srv=smbw_srvs;srv;srv=next_srv) {
1433                 next_srv = srv->next;
1434                 smbw_srv_close(srv);
1435         }
1436
1437         slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
1438         smbw_setshared(line,smbw_cwd);
1439
1440         /* unblock the parent */
1441         write(p[1], &c, 1);
1442         close(p[1]);
1443
1444         /* and continue in the child */
1445         return 0;
1446 }
1447
1448 #ifndef NO_ACL_WRAPPER
1449 /***************************************************** 
1450 say no to acls
1451 *******************************************************/
1452  int smbw_acl(const char *pathp, int cmd, int nentries, aclent_t *aclbufp)
1453 {
1454         if (cmd == GETACL || cmd == GETACLCNT) return 0;
1455         errno = ENOSYS;
1456         return -1;
1457 }
1458 #endif
1459
1460 #ifndef NO_FACL_WRAPPER
1461 /***************************************************** 
1462 say no to acls
1463 *******************************************************/
1464  int smbw_facl(int fd, int cmd, int nentries, aclent_t *aclbufp)
1465 {
1466         if (cmd == GETACL || cmd == GETACLCNT) return 0;
1467         errno = ENOSYS;
1468         return -1;
1469 }
1470 #endif
1471
1472 #ifdef HAVE_EXPLICIT_LARGEFILE_SUPPORT
1473 #ifdef HAVE_STAT64
1474 /* this can't be in wrapped.c because of include conflicts */
1475  void stat64_convert(struct stat *st, struct stat64 *st64)
1476 {
1477         st64->st_size = st->st_size;
1478         st64->st_mode = st->st_mode;
1479         st64->st_ino = st->st_ino;
1480         st64->st_dev = st->st_dev;
1481         st64->st_rdev = st->st_rdev;
1482         st64->st_nlink = st->st_nlink;
1483         st64->st_uid = st->st_uid;
1484         st64->st_gid = st->st_gid;
1485         st64->st_atime = st->st_atime;
1486         st64->st_mtime = st->st_mtime;
1487         st64->st_ctime = st->st_ctime;
1488         st64->st_blksize = st->st_blksize;
1489         st64->st_blocks = st->st_blocks;
1490 }
1491 #endif
1492
1493 #ifdef HAVE_READDIR64
1494  void dirent64_convert(struct dirent *d, struct dirent64 *d64)
1495 {
1496         d64->d_ino = d->d_ino;
1497         d64->d_off = d->d_off;
1498         d64->d_reclen = d->d_reclen;
1499         pstrcpy(d64->d_name, d->d_name);
1500 }
1501 #endif
1502 #endif
1503
1504
1505 #ifdef HAVE___XSTAT
1506 /* Definition of `struct stat' used in the linux kernel..  */
1507 struct kernel_stat {
1508         unsigned short int st_dev;
1509         unsigned short int __pad1;
1510         unsigned long int st_ino;
1511         unsigned short int st_mode;
1512         unsigned short int st_nlink;
1513         unsigned short int st_uid;
1514         unsigned short int st_gid;
1515         unsigned short int st_rdev;
1516         unsigned short int __pad2;
1517         unsigned long int st_size;
1518         unsigned long int st_blksize;
1519         unsigned long int st_blocks;
1520         unsigned long int st_atime;
1521         unsigned long int __unused1;
1522         unsigned long int st_mtime;
1523         unsigned long int __unused2;
1524         unsigned long int st_ctime;
1525         unsigned long int __unused3;
1526         unsigned long int __unused4;
1527         unsigned long int __unused5;
1528 };
1529
1530 /*
1531  * Prototype for gcc in 'fussy' mode.
1532  */
1533  void xstat_convert(int vers, struct stat *st, struct kernel_stat *kbuf);
1534  void xstat_convert(int vers, struct stat *st, struct kernel_stat *kbuf)
1535 {
1536 #ifdef _STAT_VER_LINUX_OLD
1537         if (vers == _STAT_VER_LINUX_OLD) {
1538                 memcpy(st, kbuf, sizeof(*st));
1539                 return;
1540         }
1541 #endif
1542
1543         ZERO_STRUCTP(st);
1544
1545         st->st_dev = kbuf->st_dev;
1546         st->st_ino = kbuf->st_ino;
1547         st->st_mode = kbuf->st_mode;
1548         st->st_nlink = kbuf->st_nlink;
1549         st->st_uid = kbuf->st_uid;
1550         st->st_gid = kbuf->st_gid;
1551         st->st_rdev = kbuf->st_rdev;
1552         st->st_size = kbuf->st_size;
1553         st->st_blksize = kbuf->st_blksize;
1554         st->st_blocks = kbuf->st_blocks;
1555         st->st_atime = kbuf->st_atime;
1556         st->st_mtime = kbuf->st_mtime;
1557         st->st_ctime = kbuf->st_ctime;
1558 }
1559 #endif