/* Unix SMB/Netbios implementation. Version 2.0 SMB wrapper directory functions Copyright (C) Andrew Tridgell 1998 Copyright (C) Derrell Lipman 2003-2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "smbw.h" #include "bsd-strlfunc.h" /***************************************************** determine if a directory handle is a smb one *******************************************************/ int smbw_dirp(DIR * dirp) { return ((char *) dirp >= (char *) smbw_fd_map && (char *) dirp < (char *) &smbw_fd_map[__FD_SETSIZE] && *(int *) dirp != -1); } /***************************************************** a wrapper for getdents() *******************************************************/ int smbw_getdents(unsigned int fd_smbw, struct SMBW_dirent *dirent_external, int count) { int remaining; int fd_client = smbw_fd_map[fd_smbw]; struct smbc_dirent *dirent_internal; for (remaining = count; remaining > sizeof(struct SMBW_dirent); dirent_external++) { /* * We do these one at a time because there's otherwise no way * to limit how many smbc_getdents() will return for us, and * if it returns too many, it also doesn't give us offsets to * be able to seek back to where we need to be. In practice, * this one-at-a-time retrieval isn't a problem because the * time-consuming network transaction is all done at * smbc_opendir() time. */ dirent_internal = smbc_readdir(fd_client); if (dirent_internal == NULL) { break; } remaining -= sizeof(struct SMBW_dirent); dirent_external->d_ino = -1; /* not supported */ dirent_external->d_off = smbc_telldir(fd_client); dirent_external->d_reclen = sizeof(struct SMBW_dirent); dirent_external->d_type = dirent_internal->smbc_type; smbw_strlcpy(dirent_external->d_name, dirent_internal->name, sizeof(dirent_external->d_name) - 1); smbw_strlcpy(dirent_external->d_comment, dirent_internal->comment, sizeof(dirent_external->d_comment) - 1); } return(count - remaining); } /***************************************************** a wrapper for chdir() *******************************************************/ int smbw_chdir(const char *name) { int simulate; struct stat statbuf; char path[PATH_MAX]; char *p; SMBW_INIT(); if (!name) { errno = EINVAL; return -1; } if (! smbw_path((char *) name)) { if ((* smbw_libc.chdir)(name) == 0) { *smbw_cwd = '\0'; return 0; } return -1; } smbw_fix_path(name, path); /* ensure it exists */ p = path + 6; /* look just past smb:// */ simulate = (strchr(p, '/') == NULL); /* special case for full-network scan, workgroups, and servers */ if (! simulate) { if (smbc_stat(path, &statbuf) < 0) { return -1; } /* ensure it's a directory */ if (! S_ISDIR(statbuf.st_mode)) { errno = ENOTDIR; return -1; } } smbw_strlcpy(smbw_cwd, path, PATH_MAX); /* we don't want the old directory to be busy */ (* smbw_libc.chdir)("/"); return 0; } /***************************************************** a wrapper for mkdir() *******************************************************/ int smbw_mkdir(const char *fname, mode_t mode) { char path[PATH_MAX]; if (!fname) { errno = EINVAL; return -1; } SMBW_INIT(); smbw_fix_path(fname, path); return smbc_mkdir(path, mode); } /***************************************************** a wrapper for rmdir() *******************************************************/ int smbw_rmdir(const char *fname) { char path[PATH_MAX]; if (!fname) { errno = EINVAL; return -1; } SMBW_INIT(); smbw_fix_path(fname, path); return smbc_rmdir(path); } /***************************************************** a wrapper for getcwd() *******************************************************/ char *smbw_getcwd(char *buf, size_t size) { SMBW_INIT(); if (*smbw_cwd == '\0') { return (* smbw_libc.getcwd)(buf, size); } if (buf == NULL) { if (size == 0) { size = strlen(smbw_cwd) + 1; } buf = malloc(size); if (buf == NULL) { errno = ENOMEM; return NULL; } } smbw_strlcpy(buf, smbw_cwd, size); buf[size-1] = '\0'; return buf; } /***************************************************** a wrapper for fchdir() *******************************************************/ int smbw_fchdir(int fd_smbw) { int ret; SMBW_INIT(); if (! smbw_fd(fd_smbw)) { ret = (* smbw_libc.fchdir)(fd_smbw); (void) (* smbw_libc.getcwd)(smbw_cwd, PATH_MAX); return ret; } errno = EACCES; return -1; } /***************************************************** open a directory on the server *******************************************************/ DIR *smbw_opendir(const char *fname) { int fd_client; int fd_smbw; char path[PATH_MAX]; DIR * dirp; SMBW_INIT(); if (!fname) { errno = EINVAL; return NULL; } fd_smbw = (smbw_libc.open)(SMBW_DUMMY, O_WRONLY, 0200); if (fd_smbw == -1) { errno = EMFILE; return NULL; } smbw_fix_path(fname, path); fd_client = smbc_opendir(path); if (fd_client < 0) { (* smbw_libc.close)(fd_smbw); return NULL; } smbw_fd_map[fd_smbw] = fd_client; smbw_ref(fd_client, SMBW_RCT_Increment); dirp = (DIR *) &smbw_fd_map[fd_smbw]; return dirp; } /***************************************************** read one entry from a directory *******************************************************/ struct SMBW_dirent *smbw_readdir(DIR *dirp) { int fd_smbw; int fd_client; struct smbc_dirent *dirent_internal; static struct SMBW_dirent dirent_external; fd_smbw = (int *) dirp - smbw_fd_map; fd_client = smbw_fd_map[fd_smbw]; if ((dirent_internal = smbc_readdir(fd_client)) == NULL) { return NULL; } dirent_external.d_ino = -1; /* not supported */ dirent_external.d_off = smbc_telldir(fd_client); dirent_external.d_reclen = sizeof(struct SMBW_dirent); dirent_external.d_type = dirent_internal->smbc_type; smbw_strlcpy(dirent_external.d_name, dirent_internal->name, sizeof(dirent_external.d_name) - 1); smbw_strlcpy(dirent_external.d_comment, dirent_internal->comment, sizeof(dirent_external.d_comment) - 1); return &dirent_external; } /***************************************************** read one entry from a directory in a reentrant fashion ha! samba is not re-entrant, and neither is the libsmbclient library *******************************************************/ int smbw_readdir_r(DIR *dirp, struct SMBW_dirent *__restrict entry, struct SMBW_dirent **__restrict result) { SMBW_dirent *dirent; dirent = smbw_readdir(dirp); if (dirent != NULL) { *entry = *dirent; if (result != NULL) { *result = entry; } return 0; } if (result != NULL) { *result = NULL; } return EBADF; } /***************************************************** close a DIR* *******************************************************/ int smbw_closedir(DIR *dirp) { int fd_smbw = (int *) dirp - smbw_fd_map; int fd_client = smbw_fd_map[fd_smbw]; (* smbw_libc.close)(fd_smbw); if (smbw_ref(fd_client, SMBW_RCT_Decrement) > 0) { return 0; } smbw_fd_map[fd_smbw] = -1; return smbc_closedir(fd_client); } /***************************************************** seek in a directory *******************************************************/ void smbw_seekdir(DIR *dirp, long long offset) { int fd_smbw = (int *) dirp - smbw_fd_map; int fd_client = smbw_fd_map[fd_smbw]; smbc_lseekdir(fd_client, offset); } /***************************************************** current loc in a directory *******************************************************/ long long smbw_telldir(DIR *dirp) { int fd_smbw = (int *) dirp - smbw_fd_map; int fd_client = smbw_fd_map[fd_smbw]; return (long long) smbc_telldir(fd_client); }