1/*
2   Unix SMB/CIFS implementation.
3   SMB wrapper directory functions
4   Copyright (C) Andrew Tridgell 1998
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21#include "includes.h"
22#include "realcalls.h"
23
24extern pstring smbw_cwd;
25extern fstring smbw_prefix;
26
27static struct smbw_dir *smbw_dirs;
28
29extern struct bitmap *smbw_file_bmap;
30
31extern int smbw_busy;
32
33/*****************************************************
34map a fd to a smbw_dir structure
35*******************************************************/
36struct smbw_dir *smbw_dir(int fd)
37{
38	struct smbw_dir *dir;
39
40	for (dir=smbw_dirs;dir;dir=dir->next) {
41		if (dir->fd == fd) return dir;
42	}
43	return NULL;
44}
45
46/*****************************************************
47check if a DIR* is one of ours
48*******************************************************/
49int smbw_dirp(DIR *dirp)
50{
51	struct smbw_dir *d = (struct smbw_dir *)dirp;
52	struct smbw_dir *dir;
53
54	for (dir=smbw_dirs;dir;dir=dir->next) {
55		if (dir == d) return 1;
56	}
57	return 0;
58}
59
60/*****************************************************
61free a smbw_dir structure and all entries
62*******************************************************/
63static void free_dir(struct smbw_dir *dir)
64{
65	if(!dir) return;
66
67	SAFE_FREE(dir->list);
68	SAFE_FREE(dir->path);
69	ZERO_STRUCTP(dir);
70	SAFE_FREE(dir);
71}
72
73static struct smbw_dir *cur_dir;
74
75/*****************************************************
76add a entry to a directory listing
77*******************************************************/
78static void smbw_dir_add(struct file_info *finfo, const char *mask,
79			 void *state)
80{
81	struct file_info *cdl;
82
83	DEBUG(5,("%s\n", finfo->name));
84
85	if (cur_dir->malloced == cur_dir->count) {
86		cdl = (struct file_info *)Realloc(cur_dir->list,
87							    sizeof(cur_dir->list[0])*
88							    (cur_dir->count+100));
89		if (!cdl) {
90			/* oops */
91			return;
92		}
93		cur_dir->list = cdl;
94		cur_dir->malloced += 100;
95	}
96
97	cur_dir->list[cur_dir->count] = *finfo;
98	cur_dir->count++;
99}
100
101/*****************************************************
102add a entry to a directory listing
103*******************************************************/
104static void smbw_share_add(const char *share, uint32 type,
105			   const char *comment, void *state)
106{
107	struct file_info finfo;
108
109	if (strcmp(share,"IPC$") == 0) return;
110
111	ZERO_STRUCT(finfo);
112
113	pstrcpy(finfo.name, share);
114	finfo.mode = aRONLY | aDIR;
115
116	smbw_dir_add(&finfo, NULL, NULL);
117}
118
119
120/*****************************************************
121add a server to a directory listing
122*******************************************************/
123static void smbw_server_add(const char *name, uint32 type,
124			    const char *comment, void *state)
125{
126	struct file_info finfo;
127
128	ZERO_STRUCT(finfo);
129
130	pstrcpy(finfo.name, name);
131	finfo.mode = aRONLY | aDIR;
132
133	smbw_dir_add(&finfo, NULL, NULL);
134}
135
136
137/*****************************************************
138add a entry to a directory listing
139*******************************************************/
140static void smbw_printjob_add(struct print_job_info *job)
141{
142	struct file_info finfo;
143
144	ZERO_STRUCT(finfo);
145
146	pstrcpy(finfo.name, job->name);
147	finfo.mode = aRONLY | aDIR;
148	finfo.mtime = job->t;
149	finfo.atime = job->t;
150	finfo.ctime = job->t;
151	finfo.uid = nametouid(job->user);
152	finfo.mode = aRONLY;
153	finfo.size = job->size;
154
155	smbw_dir_add(&finfo, NULL, NULL);
156}
157
158
159/*****************************************************
160open a directory on the server
161*******************************************************/
162int smbw_dir_open(const char *fname)
163{
164	fstring server, share;
165	pstring path;
166	struct smbw_server *srv=NULL;
167	struct smbw_dir *dir=NULL;
168	pstring mask;
169	int fd;
170	char *s, *p;
171
172	if (!fname) {
173		errno = EINVAL;
174		return -1;
175	}
176
177	smbw_init();
178
179	/* work out what server they are after */
180	s = smbw_parse_path(fname, server, share, path);
181
182	DEBUG(4,("dir_open share=%s\n", share));
183
184	/* get a connection to the server */
185	srv = smbw_server(server, share);
186	if (!srv) {
187		/* smbw_server sets errno */
188		goto failed;
189	}
190
191	dir = (struct smbw_dir *)malloc(sizeof(*dir));
192	if (!dir) {
193		errno = ENOMEM;
194		goto failed;
195	}
196
197	ZERO_STRUCTP(dir);
198
199	cur_dir = dir;
200
201	slprintf(mask, sizeof(mask)-1, "%s\\*", path);
202	all_string_sub(mask,"\\\\","\\",0);
203
204	if ((p=strstr(srv->server_name,"#01"))) {
205		*p = 0;
206		smbw_server_add(".",0,"", NULL);
207		smbw_server_add("..",0,"", NULL);
208		smbw_NetServerEnum(&srv->cli, srv->server_name,
209				   SV_TYPE_DOMAIN_ENUM, smbw_server_add, NULL);
210		*p = '#';
211	} else if ((p=strstr(srv->server_name,"#1D"))) {
212		DEBUG(4,("doing NetServerEnum\n"));
213		*p = 0;
214		smbw_server_add(".",0,"", NULL);
215		smbw_server_add("..",0,"", NULL);
216		smbw_NetServerEnum(&srv->cli, srv->server_name, SV_TYPE_ALL,
217				   smbw_server_add, NULL);
218		*p = '#';
219	} else if ((strcmp(srv->cli.dev,"IPC") == 0) || (strequal(share,"IPC$"))) {
220		DEBUG(4,("doing NetShareEnum\n"));
221		smbw_share_add(".",0,"", NULL);
222		smbw_share_add("..",0,"", NULL);
223		if (smbw_RNetShareEnum(&srv->cli, smbw_share_add, NULL) < 0) {
224			errno = smbw_errno(&srv->cli);
225			goto failed;
226		}
227	} else if (strncmp(srv->cli.dev,"LPT",3) == 0) {
228		smbw_share_add(".",0,"", NULL);
229		smbw_share_add("..",0,"", NULL);
230		if (cli_print_queue(&srv->cli, smbw_printjob_add) < 0) {
231			errno = smbw_errno(&srv->cli);
232			goto failed;
233		}
234	} else {
235#if 0
236		if (strcmp(path,"\\") == 0) {
237			smbw_share_add(".",0,"");
238			smbw_share_add("..",0,"");
239		}
240#endif
241		if (cli_list(&srv->cli, mask, aHIDDEN|aSYSTEM|aDIR,
242			     smbw_dir_add, NULL) < 0) {
243			errno = smbw_errno(&srv->cli);
244			goto failed;
245		}
246	}
247
248	cur_dir = NULL;
249
250	fd = open(SMBW_DUMMY, O_WRONLY);
251	if (fd == -1) {
252		errno = EMFILE;
253		goto failed;
254	}
255
256	if (bitmap_query(smbw_file_bmap, fd)) {
257		DEBUG(0,("ERROR: fd used in smbw_dir_open\n"));
258		errno = EIO;
259		goto failed;
260	}
261
262	DLIST_ADD(smbw_dirs, dir);
263
264	bitmap_set(smbw_file_bmap, fd);
265
266	dir->fd = fd;
267	dir->srv = srv;
268	dir->path = strdup(s);
269
270	DEBUG(4,("  -> %d\n", dir->count));
271
272	return dir->fd;
273
274 failed:
275	free_dir(dir);
276
277	return -1;
278}
279
280/*****************************************************
281a wrapper for fstat() on a directory
282*******************************************************/
283int smbw_dir_fstat(int fd, struct stat *st)
284{
285	struct smbw_dir *dir;
286
287	dir = smbw_dir(fd);
288	if (!dir) {
289		errno = EBADF;
290		return -1;
291	}
292
293	ZERO_STRUCTP(st);
294
295	smbw_setup_stat(st, "", dir->count*DIRP_SIZE, aDIR);
296
297	st->st_dev = dir->srv->dev;
298
299	return 0;
300}
301
302/*****************************************************
303close a directory handle
304*******************************************************/
305int smbw_dir_close(int fd)
306{
307	struct smbw_dir *dir;
308
309	dir = smbw_dir(fd);
310	if (!dir) {
311		errno = EBADF;
312		return -1;
313	}
314
315	bitmap_clear(smbw_file_bmap, dir->fd);
316	close(dir->fd);
317
318	DLIST_REMOVE(smbw_dirs, dir);
319
320	free_dir(dir);
321
322	return 0;
323}
324
325/*****************************************************
326a wrapper for getdents()
327*******************************************************/
328int smbw_getdents(unsigned int fd, struct dirent *dirp, int count)
329{
330	struct smbw_dir *dir;
331	int n=0;
332
333	smbw_busy++;
334
335	dir = smbw_dir(fd);
336	if (!dir) {
337		errno = EBADF;
338		smbw_busy--;
339		return -1;
340	}
341
342	while (count>=DIRP_SIZE && (dir->offset < dir->count)) {
343#if HAVE_DIRENT_D_OFF
344		dirp->d_off = (dir->offset+1)*DIRP_SIZE;
345#endif
346		dirp->d_reclen = DIRP_SIZE;
347		fstrcpy(&dirp->d_name[0], dir->list[dir->offset].name);
348		dirp->d_ino = smbw_inode(dir->list[dir->offset].name);
349		dir->offset++;
350		count -= dirp->d_reclen;
351#if HAVE_DIRENT_D_OFF
352		if (dir->offset == dir->count) {
353			dirp->d_off = -1;
354		}
355#endif
356		dirp = (struct dirent *)(((char *)dirp) + DIRP_SIZE);
357		n++;
358	}
359
360	smbw_busy--;
361	return n*DIRP_SIZE;
362}
363
364
365/*****************************************************
366a wrapper for chdir()
367*******************************************************/
368int smbw_chdir(const char *name)
369{
370	struct smbw_server *srv;
371	fstring server, share;
372	pstring path;
373	uint16 mode = aDIR;
374	char *cwd;
375	int len;
376
377	smbw_init();
378
379	len = strlen(smbw_prefix);
380
381	if (smbw_busy) return real_chdir(name);
382
383	smbw_busy++;
384
385	if (!name) {
386		errno = EINVAL;
387		goto failed;
388	}
389
390	DEBUG(4,("smbw_chdir(%s)\n", name));
391
392	/* work out what server they are after */
393	cwd = smbw_parse_path(name, server, share, path);
394
395	/* a special case - accept cd to /smb */
396	if (strncmp(cwd, smbw_prefix, len-1) == 0 &&
397	    cwd[len-1] == 0) {
398		goto success1;
399	}
400
401	if (strncmp(cwd,smbw_prefix,strlen(smbw_prefix))) {
402		if (real_chdir(cwd) == 0) {
403			goto success2;
404		}
405		goto failed;
406	}
407
408	/* get a connection to the server */
409	srv = smbw_server(server, share);
410	if (!srv) {
411		/* smbw_server sets errno */
412		goto failed;
413	}
414
415	if (strncmp(srv->cli.dev,"IPC",3) &&
416	    !strequal(share, "IPC$") &&
417	    strncmp(srv->cli.dev,"LPT",3) &&
418	    !smbw_getatr(srv, path,
419			 &mode, NULL, NULL, NULL, NULL, NULL)) {
420		errno = smbw_errno(&srv->cli);
421		goto failed;
422	}
423
424	if (!(mode & aDIR)) {
425		errno = ENOTDIR;
426		goto failed;
427	}
428
429 success1:
430	/* we don't want the old directory to be busy */
431	real_chdir("/");
432
433 success2:
434
435	DEBUG(4,("set SMBW_CWD to %s\n", cwd));
436
437	pstrcpy(smbw_cwd, cwd);
438
439	smbw_busy--;
440	return 0;
441
442 failed:
443	smbw_busy--;
444	return -1;
445}
446
447
448/*****************************************************
449a wrapper for lseek() on directories
450*******************************************************/
451off_t smbw_dir_lseek(int fd, off_t offset, int whence)
452{
453	struct smbw_dir *dir;
454	off_t ret;
455
456	dir = smbw_dir(fd);
457	if (!dir) {
458		errno = EBADF;
459		return -1;
460	}
461
462	switch (whence) {
463	case SEEK_SET:
464		dir->offset = offset/DIRP_SIZE;
465		break;
466	case SEEK_CUR:
467		dir->offset += offset/DIRP_SIZE;
468		break;
469	case SEEK_END:
470		dir->offset = (dir->count * DIRP_SIZE) + offset;
471		dir->offset /= DIRP_SIZE;
472		break;
473	}
474
475	ret = dir->offset * DIRP_SIZE;
476
477	DEBUG(4,("   -> %d\n", (int)ret));
478
479	return ret;
480}
481
482
483/*****************************************************
484a wrapper for mkdir()
485*******************************************************/
486int smbw_mkdir(const char *fname, mode_t mode)
487{
488	struct smbw_server *srv;
489	fstring server, share;
490	pstring path;
491
492	if (!fname) {
493		errno = EINVAL;
494		return -1;
495	}
496
497	smbw_init();
498
499	smbw_busy++;
500
501	/* work out what server they are after */
502	smbw_parse_path(fname, server, share, path);
503
504	/* get a connection to the server */
505	srv = smbw_server(server, share);
506	if (!srv) {
507		/* smbw_server sets errno */
508		goto failed;
509	}
510
511	if (!cli_mkdir(&srv->cli, path)) {
512		errno = smbw_errno(&srv->cli);
513		goto failed;
514	}
515
516	smbw_busy--;
517	return 0;
518
519 failed:
520	smbw_busy--;
521	return -1;
522}
523
524/*****************************************************
525a wrapper for rmdir()
526*******************************************************/
527int smbw_rmdir(const char *fname)
528{
529	struct smbw_server *srv;
530	fstring server, share;
531	pstring path;
532
533	if (!fname) {
534		errno = EINVAL;
535		return -1;
536	}
537
538	smbw_init();
539
540	smbw_busy++;
541
542	/* work out what server they are after */
543	smbw_parse_path(fname, server, share, path);
544
545	/* get a connection to the server */
546	srv = smbw_server(server, share);
547	if (!srv) {
548		/* smbw_server sets errno */
549		goto failed;
550	}
551
552	if (!cli_rmdir(&srv->cli, path)) {
553		errno = smbw_errno(&srv->cli);
554		goto failed;
555	}
556
557	smbw_busy--;
558	return 0;
559
560 failed:
561	smbw_busy--;
562	return -1;
563}
564
565
566/*****************************************************
567a wrapper for getcwd()
568*******************************************************/
569char *smbw_getcwd(char *buf, size_t size)
570{
571	smbw_init();
572
573	if (smbw_busy) {
574		return (char *)real_getcwd(buf, size);
575	}
576
577	smbw_busy++;
578
579	if (!buf) {
580		if (size <= 0) size = strlen(smbw_cwd)+1;
581		buf = (char *)malloc(size);
582		if (!buf) {
583			errno = ENOMEM;
584			smbw_busy--;
585			return NULL;
586		}
587	}
588
589	if (strlen(smbw_cwd) > size-1) {
590		errno = ERANGE;
591		smbw_busy--;
592		return NULL;
593	}
594
595	safe_strcpy(buf, smbw_cwd, size);
596
597	smbw_busy--;
598	return buf;
599}
600
601/*****************************************************
602a wrapper for fchdir()
603*******************************************************/
604int smbw_fchdir(unsigned int fd)
605{
606	struct smbw_dir *dir;
607	int ret;
608
609	smbw_busy++;
610
611	dir = smbw_dir(fd);
612	if (dir) {
613		smbw_busy--;
614		return chdir(dir->path);
615	}
616
617	ret = real_fchdir(fd);
618	if (ret == 0) {
619		sys_getwd(smbw_cwd);
620	}
621
622	smbw_busy--;
623	return ret;
624}
625
626/*****************************************************
627open a directory on the server
628*******************************************************/
629DIR *smbw_opendir(const char *fname)
630{
631	int fd;
632
633	smbw_busy++;
634
635	fd = smbw_dir_open(fname);
636
637	if (fd == -1) {
638		smbw_busy--;
639		return NULL;
640	}
641
642	smbw_busy--;
643
644	return (DIR *)smbw_dir(fd);
645}
646
647/*****************************************************
648read one entry from a directory
649*******************************************************/
650struct dirent *smbw_readdir(DIR *dirp)
651{
652	struct smbw_dir *d = (struct smbw_dir *)dirp;
653	static union {
654		char buf[DIRP_SIZE];
655		struct dirent de;
656	} dbuf;
657
658	if (smbw_getdents(d->fd, &dbuf.de, DIRP_SIZE) > 0)
659		return &dbuf.de;
660
661	return NULL;
662}
663
664/*****************************************************
665close a DIR*
666*******************************************************/
667int smbw_closedir(DIR *dirp)
668{
669	struct smbw_dir *d = (struct smbw_dir *)dirp;
670	return smbw_close(d->fd);
671}
672
673/*****************************************************
674seek in a directory
675*******************************************************/
676void smbw_seekdir(DIR *dirp, off_t offset)
677{
678	struct smbw_dir *d = (struct smbw_dir *)dirp;
679	smbw_dir_lseek(d->fd,offset, SEEK_SET);
680}
681
682/*****************************************************
683current loc in a directory
684*******************************************************/
685off_t smbw_telldir(DIR *dirp)
686{
687	struct smbw_dir *d = (struct smbw_dir *)dirp;
688	return smbw_dir_lseek(d->fd,0,SEEK_CUR);
689}
690