#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/queue.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <libkern/OSAtomic.h>
#include <libkern/crypto/md5.h>
#include <sys/kauth.h>
#include <sys/paths.h>
#include <sys/smb_apple.h>
#include <sys/smb_byte_order.h>
#include <sys/mchain.h>
#include <sys/msfscc.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
#include <Triggers/triggers.h>
#include <smbclient/smbclient_internal.h>
#define SMBFS_NOHASH(smp, hval) (&(smp)->sm_hash[(hval) & (smp)->sm_hashlen])
#define smbfs_hash_lock(smp) (lck_mtx_lock((smp)->sm_hashlock))
#define smbfs_hash_unlock(smp) (lck_mtx_unlock((smp)->sm_hashlock))
extern vnop_t **smbfs_vnodeop_p;
MALLOC_DEFINE(M_SMBNODE, "SMBFS node", "SMBFS vnode private part");
MALLOC_DEFINE(M_SMBNODENAME, "SMBFS nname", "SMBFS node name");
#define FNV_32_PRIME ((uint32_t) 0x01000193UL)
#define FNV1_32_INIT ((uint32_t) 33554467UL)
#define isdigit(d) ((d) >= '0' && (d) <= '9')
static int
smbfs_build_path(char *path, struct smbnode *np, size_t maxlen)
{
struct smbnode *npstack[SMBFS_MAXPATHCOMP];
struct smbnode **npp = &npstack[0];
int i, error = 0;
i = 0;
while (np->n_parent) {
if (i++ == SMBFS_MAXPATHCOMP)
return ENAMETOOLONG;
*npp++ = np;
np = np->n_parent;
}
while (i-- && !error) {
np = *--npp;
if (strlcat(path, "/", MAXPATHLEN) >= maxlen) {
error = ENAMETOOLONG;
} else {
lck_rw_lock_shared(&np->n_name_rwlock);
if (strlcat(path, (char *)np->n_name, maxlen) >= maxlen) {
error = ENAMETOOLONG;
}
lck_rw_unlock_shared(&np->n_name_rwlock);
}
}
return error;
}
static void *
smbfs_trigger_get_mount_args(vnode_t vp, __unused vfs_context_t ctx,
int *errp)
{
struct mount_url_callargs *argsp;
int error = 0;
int length;
char *url, *mountOnPath;
struct smbmount *smp = VTOSMB(vp)->n_mount;
SMB_MALLOC(argsp, struct mount_url_callargs *, sizeof (*argsp), M_SMBFSDATA, M_WAITOK);
argsp->muc_uid = smp->sm_args.uid;
SMB_MALLOC(url, char *, MAXPATHLEN, M_SMBFSDATA, M_WAITOK | M_ZERO);
strlcpy(url, "smb:", MAXPATHLEN);
if (strlcat(url, vfs_statfs(vnode_mount(vp))->f_mntfromname, MAXPATHLEN) >= MAXPATHLEN) {
error = ENAMETOOLONG;
} else {
error = smbfs_build_path(url, VTOSMB(vp), MAXPATHLEN);
}
if (error) {
lck_rw_lock_shared(&VTOSMB(vp)->n_name_rwlock);
SMBERROR("%s: URL FAILED url = %s\n", VTOSMB(vp)->n_name, url);
lck_rw_unlock_shared(&VTOSMB(vp)->n_name_rwlock);
SMB_FREE(url, M_SMBFSDATA);
SMB_FREE(argsp, M_SMBFSDATA);
*errp = error;
return (NULL);
}
SMB_MALLOC(mountOnPath, char *, MAXPATHLEN, M_SMBFSDATA, M_WAITOK | M_ZERO);
length = MAXPATHLEN;
error = vn_getpath(vp, mountOnPath, &length);
if (error) {
lck_rw_lock_shared(&VTOSMB(vp)->n_name_rwlock);
SMBERROR("%s: vn_getpath FAILED, using smbfs_build_path!\n", VTOSMB(vp)->n_name);
lck_rw_unlock_shared(&VTOSMB(vp)->n_name_rwlock);
if (strlcpy(mountOnPath, vfs_statfs(vnode_mount(vp))->f_mntonname, MAXPATHLEN) >= MAXPATHLEN) {
error = ENAMETOOLONG;
} else {
error = smbfs_build_path(mountOnPath, VTOSMB(vp), MAXPATHLEN);
}
}
if (error) {
lck_rw_lock_shared(&VTOSMB(vp)->n_name_rwlock);
SMBERROR("%s: Mount on name FAILED url = %s\n", VTOSMB(vp)->n_name, url);
lck_rw_unlock_shared(&VTOSMB(vp)->n_name_rwlock);
SMB_FREE(mountOnPath, M_SMBFSDATA);
SMB_FREE(url, M_SMBFSDATA);
SMB_FREE(argsp, M_SMBFSDATA);
*errp = error;
return (NULL);
}
lck_rw_lock_shared(&VTOSMB(vp)->n_name_rwlock);
SMBWARNING("%s: Triggering with URL = %s mountOnPath = %s\n",
VTOSMB(vp)->n_name, url, mountOnPath);
lck_rw_unlock_shared(&VTOSMB(vp)->n_name_rwlock);
argsp->muc_url = url;
argsp->muc_mountpoint = mountOnPath;
argsp->muc_opts = (smp->sm_args.altflags & SMBFS_MNT_SOFT) ? (char *)"soft" : (char *)"";
*errp = 0;
return (argsp);
}
static void
smbfs_trigger_rel_mount_args(void *data)
{
struct mount_url_callargs *argsp = data;
SMB_FREE(argsp->muc_url, M_SMBFSDATA);
SMB_FREE(argsp->muc_mountpoint, M_SMBFSDATA);
SMB_FREE(argsp, M_SMBFSDATA);
}
static int
smb_check_for_windows_symlink(struct smb_share *share, struct smbnode *np,
int *symlen, vfs_context_t context)
{
uio_t uio;
MD5_CTX md5;
char m5b[SMB_SYMMD5LEN];
uint32_t state[4];
int len = 0;
unsigned char *sb, *cp;
uint16_t fid = 0;
int error, cerror;
error = smbfs_tmpopen(share, np, SMB2_FILE_READ_DATA, &fid, context);
if (error) {
return error;
}
SMB_MALLOC(sb, void *, (size_t)np->n_size, M_TEMP, M_WAITOK);
uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
if (uio) {
uio_addiov(uio, CAST_USER_ADDR_T(sb), np->n_size);
error = smb_read(share, fid, uio, context);
uio_free(uio);
}
else {
error = ENOMEM;
}
cerror = smbfs_tmpclose(share, np, fid, context);
if (cerror) {
SMBWARNING("error %d closing fid %d file %s\n", cerror, fid, np->n_name);
}
if (!error && !bcmp(sb, smb_symmagic, SMB_SYMMAGICLEN)) {
for (cp = &sb[SMB_SYMMAGICLEN]; cp < &sb[SMB_SYMMAGICLEN+SMB_SYMLENLEN-1]; cp++) {
if (!isdigit(*cp))
break;
len *= 10;
len += *cp - '0';
}
cp++;
if ((cp != &sb[SMB_SYMMAGICLEN+SMB_SYMLENLEN]) ||
(len > (int)(np->n_size - SMB_SYMHDRLEN))) {
SMBWARNING("bad symlink length\n");
error = ENOENT;
} else {
MD5Init(&md5);
MD5Update(&md5, &sb[SMB_SYMHDRLEN], len);
MD5Final((u_char *)state, &md5);
(void)snprintf(m5b, sizeof(m5b), "%08x%08x%08x%08x",
htobel(state[0]), htobel(state[1]), htobel(state[2]),
htobel(state[3]));
if (bcmp(cp, m5b, SMB_SYMMD5LEN-1)) {
SMBWARNING("bad symlink md5\n");
error = ENOENT;
} else {
*symlen = len;
error = 0;
}
}
}
else {
error = ENOENT;
}
SMB_FREE(sb, M_TEMP);
return error;
}
int
smbnode_lock(struct smbnode *np, enum smbfslocktype locktype)
{
if (locktype == SMBFS_SHARED_LOCK)
lck_rw_lock_shared(&np->n_rwlock);
else
lck_rw_lock_exclusive(&np->n_rwlock);
np->n_lockState = locktype;
#if 1
if (locktype != SMBFS_SHARED_LOCK) {
np->n_activation = (void *) current_thread();
}
#endif
return (0);
}
int
smbnode_lockpair(struct smbnode *np1, struct smbnode *np2, enum smbfslocktype locktype)
{
int error;
if (np1 == np2) {
return smbnode_lock(np1, locktype);
}
if ((error = smbnode_lock(np1, locktype)))
return (error);
if ((error = smbnode_lock(np2, locktype))) {
smbnode_unlock(np1);
return (error);
}
return (0);
}
void
smbnode_unlock(struct smbnode *np)
{
if (np->n_lockState == SMBFS_SHARED_LOCK) {
lck_rw_unlock_shared(&np->n_rwlock);
} else {
np->n_lockState = 0;
lck_rw_unlock_exclusive(&np->n_rwlock);
}
}
void
smbnode_unlockpair(struct smbnode *np1, struct smbnode *np2)
{
smbnode_unlock(np1);
if (np2 != np1)
smbnode_unlock(np2);
}
static int
tolower(unsigned char ch)
{
if (ch >= 'A' && ch <= 'Z')
ch = 'a' + (ch - 'A');
return ch;
}
uint32_t
smbfs_hash(const char *name, size_t nmlen)
{
uint32_t v;
for (v = FNV1_32_INIT; nmlen; name++, nmlen--) {
v *= FNV_32_PRIME;
v ^= (uint32_t)tolower((unsigned char)*name);
}
return v;
}
void
smb_vhashrem(struct smbnode *np)
{
smbfs_hash_lock(np->n_mount);
if (np->n_hash.le_prev) {
LIST_REMOVE(np, n_hash);
np->n_hash.le_prev = NULL;
}
smbfs_hash_unlock(np->n_mount);
return;
}
void
smb_vhashadd(struct smbnode *np, uint32_t hashval)
{
struct smbnode_hashhead *nhpp;
smbfs_hash_lock(np->n_mount);
nhpp = SMBFS_NOHASH(np->n_mount, hashval);
LIST_INSERT_HEAD(nhpp, np, n_hash);
smbfs_hash_unlock(np->n_mount);
return;
}
static vnode_t
smb_hashget(struct smbmount *smp, struct smbnode *dnp, uint32_t hashval,
const char *name, size_t nmlen, size_t maxfilenamelen,
uint32_t node_flag, const char *sname)
{
vnode_t vp;
struct smbnode_hashhead *nhpp;
struct smbnode *np;
uint32_t vid;
size_t snmlen = (sname) ? strnlen(sname, maxfilenamelen+1) : 0;
loop:
smbfs_hash_lock(smp);
nhpp = SMBFS_NOHASH(smp, hashval);
LIST_FOREACH(np, nhpp, n_hash) {
if ((np->n_flag & N_ISSTREAM) != node_flag)
continue;
if ((np->n_parent != dnp) || (np->n_nmlen != nmlen) ||
(strncasecmp(name, np->n_name, nmlen) != 0)) {
continue;
}
if (np->n_flag & N_ISSTREAM) {
DBG_ASSERT(sname);
if ((np->n_snmlen != snmlen) ||
(bcmp(sname, np->n_sname, snmlen) != 0)) {
SMBERROR("We only support one stream and we found found %s looking for %s\n",
np->n_sname, sname);
continue;
}
}
if (ISSET(np->n_flag, NALLOC)) {
SET(np->n_flag, NWALLOC);
(void)msleep((caddr_t)np, smp->sm_hashlock, PINOD|PDROP, "smb_ngetalloc", 0);
goto loop;
}
if (ISSET(np->n_flag, NTRANSIT)) {
SET(np->n_flag, NWTRANSIT);
(void)msleep((caddr_t)np, smp->sm_hashlock, PINOD|PDROP, "smb_ngettransit", 0);
goto loop;
}
vp = SMBTOV(np);
vid = vnode_vid(vp);
smbfs_hash_unlock(smp);
if (vnode_getwithvid(vp, vid))
return (NULL);
if ((smbnode_lock(np, SMBFS_EXCLUSIVE_LOCK)) != 0) {
vnode_put(vp);
return (NULL);
}
np->n_lastvop = smb_hashget;
return (vp);
}
smbfs_hash_unlock(smp);
return (NULL);
}
static int
node_vtype_changed(vnode_t vp, enum vtype node_vtype, struct smbfattr *fap)
{
int rt_value = FALSE;
if (vnode_isvroot(vp))
return FALSE;
if (vnode_isnamedstream(vp))
return FALSE;
if (fap->fa_valid_mask & FA_VTYPE_VALID) {
if ((VTOSMB(vp)->n_flag & NWINDOWSYMLNK) && (fap->fa_vtype == VREG)) {
rt_value = FALSE;
} else {
rt_value = (fap->fa_vtype != node_vtype);
}
goto done;
}
if (((node_vtype == VDIR) && !(VTOSMB(vp)->n_dosattr & SMB_EFA_DIRECTORY)) ||
((node_vtype != VDIR) && (VTOSMB(vp)->n_dosattr & SMB_EFA_DIRECTORY))) {
rt_value = TRUE;
goto done;
}
if ((VTOSMB(vp)->n_dosattr & SMB_EFA_REPARSE_POINT) != (fap->fa_attr & SMB_EFA_REPARSE_POINT)) {
rt_value = TRUE;
goto done;
}
done:
if (rt_value) {
SMBWARNING("%s had node type and attr of %d 0x%x now its %d 0x%x\n",
VTOSMB(vp)->n_name, node_vtype, fap->fa_vtype,
VTOSMB(vp)->n_dosattr, fap->fa_attr);
}
return rt_value;
}
int
smbfs_nget(struct smb_share *share, struct mount *mp, vnode_t dvp, const char *name,
size_t nmlen, struct smbfattr *fap, vnode_t *vpp, uint32_t cnflags,
vfs_context_t context)
{
struct smbmount *smp = VFSTOSMBFS(mp);
struct smbnode *np, *dnp;
vnode_t vp;
int error = 0;
uint32_t hashval;
struct vnode_fsparam vfsp;
int locked = 0;
struct componentname cnp;
trigger_info_t *ti;
*vpp = NULL;
if (vfs_isforce(mp))
return ENXIO;
if (smp->sm_rvp != NULL && dvp == NULL) {
return EINVAL;
}
if (nmlen == 2 && bcmp(name, "..", 2) == 0) {
SMBDEBUG("do not call me with dotdot!\n");
return EINVAL;
} else if (nmlen == 1 && name[0] == '.') {
SMBDEBUG("do not call me with dot!\n");
return (EINVAL);
}
dnp = dvp ? VTOSMB(dvp) : NULL;
if (dnp == NULL && dvp != NULL) {
SMBDEBUG("dead parent vnode\n");
return (EINVAL);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameptr = (char *)name;
cnp.cn_namelen = (int)nmlen;
cnp.cn_flags = cnflags;
SMB_MALLOC(np, struct smbnode *, sizeof *np, M_SMBNODE, M_WAITOK | M_ZERO);
hashval = smbfs_hash(name, nmlen);
if ((*vpp = smb_hashget(smp, dnp, hashval, name, nmlen,
share->ss_maxfilenamelen, 0, NULL)) != NULL) {
DBG_ASSERT(!vnode_isnamedstream(*vpp));
if (fap && node_vtype_changed(*vpp, vnode_vtype(*vpp), fap)) {
cache_purge(*vpp);
smb_vhashrem(VTOSMB(*vpp));
VTOSMB(*vpp)->attribute_cache_timer = 0;
VTOSMB(*vpp)->n_symlink_cache_timer = 0;
smbnode_unlock(VTOSMB(*vpp));
vnode_put(*vpp);
*vpp = NULL;
} else {
SMB_FREE(np, M_SMBNODE);
if (fap)
smbfs_attr_cacheenter(share, *vpp, fap, FALSE, context);
if (dvp && (cnp.cn_flags & MAKEENTRY))
cache_enter(dvp, *vpp, &cnp);
return (0);
}
}
if (fap == NULL) {
SMB_FREE(np, M_SMBNODE);
return (ENOENT);
}
lck_rw_init(&np->n_rwlock, smbfs_rwlock_group, smbfs_lock_attr);
lck_rw_init(&np->n_name_rwlock, smbfs_rwlock_group, smbfs_lock_attr);
(void) smbnode_lock(np, SMBFS_EXCLUSIVE_LOCK);
locked = 1;
np->n_lastvop = smbfs_nget;
np->n_reparse_tag = fap->fa_reparse_tag;
np->n_dosattr = fap->fa_attr;
np->n_vnode = NULL;
np->n_mount = smp;
np->n_size = fap->fa_size;
np->n_data_alloc = fap->fa_data_alloc;
np->n_ino = fap->fa_ino;
np->n_name = smb_strndup(name, nmlen);
np->n_nmlen = nmlen;
np->n_flags_mask = EXT_REQUIRED_BY_MAC;
np->n_uid = KAUTH_UID_NONE;
np->n_gid = KAUTH_GID_NONE;
np->n_nfs_uid = KAUTH_UID_NONE;
np->n_nfs_gid = KAUTH_GID_NONE;
SET(np->n_flag, NALLOC);
smb_vhashadd(np, hashval);
if (dvp) {
np->n_parent = dnp;
if (!vnode_isvroot(dvp)) {
if (vnode_get(dvp) == 0) {
if (vnode_ref(dvp) == 0) {
np->n_flag |= NREFPARENT;
vnode_put(dvp);
OSIncrementAtomic(&dnp->n_child_refcnt);
} else {
vnode_put(dvp);
error = EINVAL;
goto errout;
}
} else {
error = EINVAL;
goto errout;
}
}
}
vfsp.vnfs_mp = mp;
vfsp.vnfs_vtype = fap->fa_vtype;
vfsp.vnfs_str = "smbfs";
vfsp.vnfs_dvp = dvp;
vfsp.vnfs_fsnode = np;
vfsp.vnfs_cnp = &cnp;
vfsp.vnfs_vops = smbfs_vnodeop_p;
vfsp.vnfs_rdev = 0;
vfsp.vnfs_flags = (dvp && (cnp.cn_flags & MAKEENTRY)) ? 0 : VNFS_NOCACHE;
vfsp.vnfs_markroot = (np->n_ino == 2);
vfsp.vnfs_marksystem = 0;
if ((vfsp.vnfs_vtype == VDIR) && (dvp == NULL) && (smp->sm_rvp == NULL) &&
(np->n_ino == 2)) {
error = smbfs_lookup(share, np, NULL, NULL, fap, context);
if (error)
goto errout;
} else if ((vfsp.vnfs_vtype == VREG) && (np->n_size == SMB_SYMLEN)) {
int symlen = 0;
DBG_ASSERT(dvp);
if (smb_check_for_windows_symlink(share, np, &symlen, context) == 0) {
vfsp.vnfs_vtype = VLNK;
fap->fa_valid_mask |= FA_VTYPE_VALID;
fap->fa_vtype = VLNK;
np->n_size = symlen;
np->n_flag |= NWINDOWSYMLNK;
}
}
vfsp.vnfs_filesize = np->n_size;
if ((np->n_dosattr & SMB_EFA_REPARSE_POINT) &&
(np->n_reparse_tag != IO_REPARSE_TAG_DFS) &&
(np->n_reparse_tag != IO_REPARSE_TAG_SYMLINK)) {
SMBWARNING("%s - reparse point tag 0x%x\n", np->n_name, np->n_reparse_tag);
}
if ((np->n_dosattr & SMB_EFA_REPARSE_POINT) &&
(np->n_reparse_tag == IO_REPARSE_TAG_DFS)) {
struct vnode_trigger_param vtp;
bcopy(&vfsp, &vtp.vnt_params, sizeof(vfsp));
ti = trigger_new(&vtp, smbfs_trigger_get_mount_args, smbfs_trigger_rel_mount_args);
error = vnode_create(VNCREATE_TRIGGER, (uint32_t)VNCREATE_TRIGGER_SIZE, &vtp, &vp);
if (error)
trigger_free(ti);
} else {
error = vnode_create(VNCREATE_FLAVOR, (uint32_t)VCREATESIZE, &vfsp, &vp);
}
if (error)
goto errout;
vnode_settag(vp, VT_CIFS);
np->n_vnode = vp;
switch (vnode_vtype(vp)) {
case VREG:
np->n_mode |= S_IFREG;
break;
case VLNK:
np->n_mode |= S_IFLNK;
break;
case VDIR:
np->n_mode |= S_IFDIR;
break;
default:
SMBERROR("vnode_vtype %d\n", vnode_vtype(vp));
np->n_mode |= S_IFREG;
}
if (!vnode_isdir(vp)) {
lck_mtx_init(&np->f_openStateLock, smbfs_mutex_group, smbfs_lock_attr);
lck_mtx_init(&np->f_clusterWriteLock, smbfs_mutex_group, smbfs_lock_attr);
lck_mtx_init(&np->rfrkMetaLock, smbfs_mutex_group, smbfs_lock_attr);
lck_mtx_init(&np->f_openDenyListLock, smbfs_mutex_group, smbfs_lock_attr);
}
lck_mtx_init(&np->f_ACLCacheLock, smbfs_mutex_group, smbfs_lock_attr);
smbfs_attr_cacheenter(share, vp, fap, FALSE, context);
*vpp = vp;
CLR(np->n_flag, NALLOC);
if (ISSET(np->n_flag, NWALLOC))
wakeup(np);
return 0;
errout:
if (np->n_flag & NREFPARENT) {
if (vnode_get(dvp) == 0) {
vnode_rele(dvp);
vnode_put(dvp);
}
np->n_flag &= ~NREFPARENT;
OSDecrementAtomic(&dnp->n_child_refcnt);
}
smb_vhashrem(np);
if (locked == 1)
smbnode_unlock(np);
if (ISSET(np->n_flag, NWALLOC))
wakeup(np);
SMB_FREE(np->n_name, M_SMBNODENAME);
SMB_FREE(np, M_SMBNODE);
return error;
}
vnode_t
smbfs_find_vgetstrm(struct smbmount *smp, struct smbnode *np, const char *sname,
size_t maxfilenamelen)
{
uint32_t hashval;
hashval = smbfs_hash(np->n_name, np->n_nmlen);
return(smb_hashget(smp, np, hashval, np->n_name, np->n_nmlen, maxfilenamelen,
N_ISSTREAM, sname));
}
int
smbfs_vgetstrm(struct smb_share *share, struct smbmount *smp, vnode_t vp,
vnode_t *svpp, struct smbfattr *fap, const char *sname)
{
struct smbnode *np, *snp;
int error = 0;
uint32_t hashval;
struct vnode_fsparam vfsp;
int locked = 0;
struct componentname cnp;
size_t maxfilenamelen = share->ss_maxfilenamelen;
DBG_ASSERT(smp->sm_rvp);
DBG_ASSERT(vp);
DBG_ASSERT((!vnode_isdir(vp)));
DBG_ASSERT(!vnode_isnamedstream(vp));
np = VTOSMB(vp);
*svpp = NULL;
if (vfs_isforce(smp->sm_mp))
return ENXIO;
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = LOOKUP;
cnp.cn_flags = ISLASTCN;
cnp.cn_pnlen = MAXPATHLEN;
SMB_MALLOC (cnp.cn_pnbuf, caddr_t, MAXPATHLEN, M_TEMP, M_WAITOK);
if (bcmp(sname, SFM_RESOURCEFORK_NAME, sizeof(SFM_RESOURCEFORK_NAME)) == 0) {
cnp.cn_nameptr = cnp.cn_pnbuf;
cnp.cn_namelen = snprintf(cnp.cn_nameptr, MAXPATHLEN, "%s%s", np->n_name,
_PATH_RSRCFORKSPEC);
} else {
cnp.cn_nameptr = cnp.cn_pnbuf;
cnp.cn_namelen = snprintf(cnp.cn_nameptr, MAXPATHLEN, "%s%s%s", np->n_name,
_PATH_FORKSPECIFIER, sname);
SMBWARNING("Creating non resource fork named stream: %s\n", cnp.cn_nameptr);
}
SMB_MALLOC(snp, struct smbnode *, sizeof *snp, M_SMBNODE, M_WAITOK);
hashval = smbfs_hash(np->n_name, np->n_nmlen);
if ((*svpp = smb_hashget(smp, np, hashval, np->n_name, np->n_nmlen,
maxfilenamelen, N_ISSTREAM, sname)) != NULL) {
SMB_FREE(snp, M_SMBNODE);
smbfs_attr_cacheenter(share, *svpp, fap, FALSE, NULL);
goto done;
}
bzero(snp, sizeof(*snp));
lck_rw_init(&snp->n_rwlock, smbfs_rwlock_group, smbfs_lock_attr);
lck_rw_init(&snp->n_name_rwlock, smbfs_rwlock_group, smbfs_lock_attr);
(void) smbnode_lock(snp, SMBFS_EXCLUSIVE_LOCK);
locked = 1;
snp->n_lastvop = smbfs_vgetstrm;
snp->n_mount = smp;
snp->n_size = fap->fa_size;
snp->n_data_alloc = fap->fa_data_alloc;
snp->n_ino = np->n_ino;
snp->n_name = smb_strndup(np->n_name, np->n_nmlen);
snp->n_nmlen = np->n_nmlen;
snp->n_flags_mask = np->n_flags_mask;
snp->n_uid = np->n_uid;
snp->n_gid = np->n_gid;
snp->n_nfs_uid = np->n_nfs_uid;
snp->n_nfs_gid = np->n_nfs_uid;
snp->n_parent = np;
snp->n_snmlen = strnlen(sname, maxfilenamelen+1);
snp->n_sname = smb_strndup(sname, snp->n_snmlen);
SET(snp->n_flag, N_ISSTREAM);
if (bcmp(sname, SFM_RESOURCEFORK_NAME, sizeof(SFM_RESOURCEFORK_NAME)) == 0)
SET(snp->n_flag, N_ISRSRCFRK);
SET(snp->n_flag, NALLOC);
smb_vhashadd(snp, hashval);
#ifdef _NOT_YET_
if (!vnode_isvroot(vp)) {
if (vnode_get(vp) == 0) {
if (vnode_ref(vp) == 0) {
snp->n_flag |= NREFPARENT;
vnode_put(vp);
OSIncrementAtomic(&np->n_child_refcnt);
} else {
vnode_put(vp);
error = EINVAL;
goto errout;
}
} else {
error = EINVAL;
goto errout;
}
}
#endif
vfsp.vnfs_mp = smp->sm_mp;
vfsp.vnfs_vtype = VREG;
vfsp.vnfs_str = "smbfs";
vfsp.vnfs_dvp = NULL;
vfsp.vnfs_fsnode = snp;
vfsp.vnfs_cnp = &cnp;
vfsp.vnfs_vops = smbfs_vnodeop_p;
vfsp.vnfs_rdev = 0;
vfsp.vnfs_flags = VNFS_NOCACHE;
vfsp.vnfs_markroot = 0;
vfsp.vnfs_marksystem = 0;
vfsp.vnfs_filesize = fap->fa_size;
error = vnode_create(VNCREATE_FLAVOR, (uint32_t)VCREATESIZE, &vfsp, svpp);
if (error)
goto errout;
vnode_settag(*svpp, VT_CIFS);
snp->n_vnode = *svpp;
snp->n_mode = S_IFREG | (np->n_mode & ACCESSPERMS);
lck_mtx_init(&snp->f_openStateLock, smbfs_mutex_group, smbfs_lock_attr);
lck_mtx_init(&snp->f_clusterWriteLock, smbfs_mutex_group, smbfs_lock_attr);
lck_mtx_init(&snp->f_openDenyListLock, smbfs_mutex_group, smbfs_lock_attr);
smbfs_attr_cacheenter(share, *svpp, fap, FALSE, NULL);
CLR(snp->n_flag, NALLOC);
if (ISSET(snp->n_flag, NWALLOC))
wakeup(snp);
goto done;
errout:
#ifdef _NOT_YET_
if (snp->n_flag & NREFPARENT) {
if (vnode_get(vp) == 0) {
vnode_rele(vp);
vnode_put(vp);
}
snp->n_flag &= ~NREFPARENT;
OSDecrementAtomic(&np->n_child_refcnt);
}
#endif
smb_vhashrem(snp);
if (locked == 1)
smbnode_unlock(snp);
if (ISSET(snp->n_flag, NWALLOC))
wakeup(snp);
SMB_FREE(snp->n_name, M_SMBNODENAME);
SMB_FREE(snp->n_sname, M_SMBNODENAME);
SMB_FREE(snp, M_SMBNODE);
done:
SMB_FREE(cnp.cn_pnbuf, M_TEMP);
return error;
}
int
smb_get_rsrcfrk_size(struct smb_share *share, vnode_t vp, vfs_context_t context)
{
struct smbnode *np = VTOSMB(vp);
uint64_t strmsize = 0;
time_t attrtimeo;
struct timespec ts;
int error = 0;
time_t rfrk_cache_timer;
struct timespec reqtime;
nanouptime(&reqtime);
SMB_CACHE_TIME(ts, np, attrtimeo);
lck_mtx_lock(&np->rfrkMetaLock);
rfrk_cache_timer = ts.tv_sec - np->rfrk_cache_timer;
lck_mtx_unlock(&np->rfrkMetaLock);
if (rfrk_cache_timer > attrtimeo) {
error = smbfs_smb_qstreaminfo(share, np, NULL, NULL,
SFM_RESOURCEFORK_NAME, &strmsize, context);
if (error == 0) {
struct smbmount *smp = VTOSMBFS(vp);
vnode_t svpp = smbfs_find_vgetstrm(smp, np, SFM_RESOURCEFORK_NAME,
share->ss_maxfilenamelen);
if (svpp) {
if (smbfs_update_size(VTOSMB(svpp), &reqtime, strmsize) == TRUE) {
nanouptime(&ts);
VTOSMB(svpp)->attribute_cache_timer = ts.tv_sec;
}
smbnode_unlock(VTOSMB(svpp));
vnode_put(svpp);
}
} else {
lck_mtx_lock(&np->rfrkMetaLock);
np->rfrk_size = 0;
nanouptime(&ts);
np->rfrk_cache_timer = ts.tv_sec;
lck_mtx_unlock(&np->rfrkMetaLock);
}
}
return(error);
}
vnode_t
smb_update_rsrc_and_getparent(vnode_t vp, int setsize)
{
struct smbnode *np = VTOSMB(vp);
vnode_t parent_vp = vnode_getparent(vp);
struct timespec ts;
if ((parent_vp) && (np->n_flag & N_ISRSRCFRK)) {
lck_mtx_lock(&VTOSMB(parent_vp)->rfrkMetaLock);
if (setsize) {
VTOSMB(parent_vp)->rfrk_size = np->n_size;
nanouptime(&ts);
VTOSMB(parent_vp)->rfrk_cache_timer = ts.tv_sec;
} else if (VTOSMB(parent_vp)->rfrk_size != np->n_size) {
VTOSMB(parent_vp)->rfrk_cache_timer = 0;
}
lck_mtx_unlock(&VTOSMB(parent_vp)->rfrkMetaLock);
}
return(parent_vp);
}
static int
smb_gid_match(struct smbmount *smp, u_int64_t node_gid)
{
u_int32_t ii;
if (node_gid == smp->ntwrk_gid)
return TRUE;
for (ii=0; ii < smp->ntwrk_cnt_gid; ii++)
if (node_gid == smp->ntwrk_gids[ii])
return TRUE;
return FALSE;
}
int
smb_check_posix_access(vfs_context_t context, struct smbnode * np,
mode_t rq_mode)
{
kauth_cred_t cred = vfs_context_ucred(context);
uid_t user = kauth_cred_getuid (cred);
int inGroup = 0;
kauth_cred_ismember_gid(cred, np->n_gid, &inGroup);
if (user == np->n_uid) {
if (np->n_mode & (rq_mode << 6))
return TRUE;
} else if (inGroup) {
if (np->n_mode & (rq_mode << 3))
return TRUE;
} else {
if (np->n_mode & rq_mode)
return TRUE;
}
return FALSE;
}
Boolean
node_isimmutable(struct smb_share *share, vnode_t vp)
{
Boolean unix_info2 = ((UNIX_CAPS(share) & UNIX_QFILEINFO_UNIX_INFO2_CAP)) ? TRUE : FALSE;
Boolean darwin = (SSTOVC(share)->vc_flags & SMBV_DARWIN) ? TRUE : FALSE;
if ((unix_info2 || darwin || !vnode_isdir(vp)) &&
(VTOSMB(vp)->n_dosattr & SMB_EFA_RDONLY)) {
return TRUE;
}
return FALSE;
}
void
smbfs_attr_cacheenter(struct smb_share *share, vnode_t vp, struct smbfattr *fap,
int UpdateResourceParent, vfs_context_t context)
{
struct smbmount *smp = VTOSMBFS(vp);
struct smbnode *np = VTOSMB(vp);
enum vtype node_vtype;
struct timespec ts;
uint32_t monitorHint = 0;
node_vtype = vnode_vtype(vp);
if ((node_vtype == VDIR) && np->d_needsUpdate) {
monitorHint |= VNODE_EVENT_ATTRIB | VNODE_EVENT_WRITE;
np->d_needsUpdate = FALSE;
}
if (node_vtype_changed(vp, node_vtype, fap)) {
np->attribute_cache_timer = 0;
np->n_symlink_cache_timer = 0;
cache_purge(vp);
smb_vhashrem(np);
monitorHint |= VNODE_EVENT_RENAME | VNODE_EVENT_ATTRIB;
goto vnode_notify_needed;
}
np->n_flag &= ~NATTRCHANGED;
if (node_vtype == VREG) {
if (smbfs_update_size(np, &fap->fa_reqtime, fap->fa_size) == FALSE) {
fap->fa_size = np->n_size;
np->n_flag |= NATTRCHANGED;
} else if (np->n_size != fap->fa_size) {
monitorHint |= VNODE_EVENT_EXTEND | VNODE_EVENT_ATTRIB;
}
} else if (node_vtype == VDIR) {
np->n_size = 16384;
if ((np->n_flag & NNEGNCENTRIES) &&
((share->ss_fstype == SMB_FS_FAT) ||
(timespeccmp(&fap->fa_mtime, &np->n_mtime, >)))) {
np->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(vp);
}
if (timespeccmp(&fap->fa_mtime, &np->n_mtime, <))
fap->fa_mtime = np->n_mtime;
} else if (node_vtype != VLNK) {
return;
}
np->n_data_alloc = fap->fa_data_alloc;
if (fap->fa_unix) {
np->n_flags_mask = fap->fa_flags_mask;
np->n_nlinks = fap->fa_nlinks;
if ((fap->fa_valid_mask & FA_UNIX_MODES_VALID) != FA_UNIX_MODES_VALID) {
if (np->n_uid == KAUTH_UID_NONE) {
np->n_uid = smp->sm_args.uid;
if (vnode_isdir(np->n_vnode)) {
np->n_mode |= smp->sm_args.dir_mode;
} else {
np->n_mode |= smp->sm_args.file_mode;
}
}
if (np->n_uid == KAUTH_GID_NONE) {
np->n_uid = smp->sm_args.gid;
}
} else if (smp->sm_args.altflags & SMBFS_MNT_TIME_MACHINE) {
np->n_mode &= ~ACCESSPERMS;
np->n_uid = smp->sm_args.uid;
np->n_gid = smp->sm_args.gid;
np->n_mode |= (mode_t)(fap->fa_permissions & ACCESSPERMS);
} else if (share->ss_attributes & FILE_PERSISTENT_ACLS) {
np->n_mode &= ~ACCESSPERMS;
if ((smp->sm_flags & MNT_MAPS_NETWORK_LOCAL_USER) &&
(smp->ntwrk_uid == fap->fa_uid)) {
np->n_uid = smp->sm_args.uid;
np->n_gid = smp->sm_args.gid;
} else {
np->n_uid = (uid_t)fap->fa_uid;
np->n_gid = (gid_t)fap->fa_gid;
}
np->n_mode |= (mode_t)(fap->fa_permissions & ACCESSPERMS);
} else if ((fap->fa_permissions & ACCESSPERMS) &&
(smp->sm_args.uid == (uid_t)smp->ntwrk_uid) &&
(smp->sm_args.gid == (gid_t)smp->ntwrk_gid)) {
np->n_mode &= ~ACCESSPERMS;
np->n_uid = (uid_t)fap->fa_uid;
np->n_gid = (gid_t)fap->fa_gid;
np->n_mode |= (mode_t)(fap->fa_permissions & ACCESSPERMS);
} else {
int uid_match = (fap->fa_uid == smp->ntwrk_uid);
int gid_match = smb_gid_match(smp, fap->fa_gid);
np->n_mode &= ~ACCESSPERMS;
np->n_uid = smp->sm_args.uid;
np->n_gid = smp->sm_args.gid;
if ((fap->fa_permissions & ACCESSPERMS) == 0)
fap->fa_permissions = ACCESSPERMS;
if (!uid_match && !gid_match) {
np->n_mode |= (mode_t)(fap->fa_permissions & S_IRWXO);
np->n_mode |= (mode_t)((fap->fa_permissions & S_IRWXO) << 3);
np->n_mode |= (mode_t)((fap->fa_permissions & S_IRWXO) << 6);
} else if (!uid_match && gid_match) {
np->n_mode |= (mode_t)(fap->fa_permissions & (S_IRWXG | S_IRWXO));
np->n_mode |= (mode_t)((fap->fa_permissions & S_IRWXG) << 3);
} else if (uid_match && !gid_match) {
np->n_mode |= (mode_t)(fap->fa_permissions & (S_IRWXU | S_IRWXO));
np->n_mode |= (mode_t)((fap->fa_permissions & S_IRWXO) << 3);
} else {
np->n_mode |= (mode_t)(fap->fa_permissions & ACCESSPERMS);
}
}
} else {
if (!(share->ss_attributes & FILE_PERSISTENT_ACLS) &&
((np->n_uid == KAUTH_UID_NONE) || (np->n_gid == KAUTH_GID_NONE))) {
np->n_uid = smp->sm_args.uid;
np->n_gid = smp->sm_args.gid;
}
if (!(np->n_flag & NHAS_POSIXMODES)) {
np->n_mode &= ~ACCESSPERMS;
if (vnode_vtype(vp) == VDIR)
np->n_mode |= smp->sm_args.dir_mode;
else
np->n_mode |= smp->sm_args.file_mode;
}
}
if ((monitorHint & VNODE_EVENT_ATTRIB) == 0) {
if (!(timespeccmp(&np->n_crtime, &fap->fa_crtime, ==) ||
!(timespeccmp(&np->n_mtime, &fap->fa_mtime, ==))))
monitorHint |= VNODE_EVENT_ATTRIB;
}
if (fap->fa_valid_mask & FA_FSTATUS_VALID) {
np->n_fstatus = fap->fa_fstatus;
} else if (timespeccmp(&np->n_chtime, &fap->fa_chtime, !=)) {
np->n_fstatus = 0;
}
np->n_crtime = fap->fa_crtime;
np->n_chtime = fap->fa_chtime;
np->n_atime = fap->fa_atime;
np->n_mtime = fap->fa_mtime;
if (fap->fa_unix && ((fap->fa_flags_mask & EXT_REQUIRED_BY_MAC) != EXT_REQUIRED_BY_MAC)) {
fap->fa_attr &= ~(SMB_EFA_RDONLY | SMB_EFA_HIDDEN | SMB_EFA_ARCHIVE);
np->n_dosattr &= (SMB_EFA_RDONLY | SMB_EFA_HIDDEN | SMB_EFA_ARCHIVE);
np->n_dosattr |= fap->fa_attr;
} else {
np->n_dosattr = fap->fa_attr;
}
nanouptime(&ts);
np->attribute_cache_timer = ts.tv_sec;
if (UpdateResourceParent && (vnode_isnamedstream(vp)) &&
(np->n_flag & N_ISRSRCFRK)) {
vnode_t parent_vp = smb_update_rsrc_and_getparent(vp, (fap->fa_size) ? TRUE : FALSE);
if (parent_vp)
vnode_put(parent_vp);
}
vnode_notify_needed:
if ((monitorHint != 0) && (vnode_ismonitored(vp)) && context) {
struct vnode_attr vattr;
vfs_get_notify_attributes(&vattr);
smbfs_attr_cachelookup(share, vp, &vattr, context, TRUE);
vnode_notify(vp, monitorHint, &vattr);
}
}
int
smbfs_attr_cachelookup(struct smb_share *share, vnode_t vp, struct vnode_attr *va,
vfs_context_t context, int useCacheDataOnly)
{
struct smbnode *np = VTOSMB(vp);
struct smbmount *smp = VTOSMBFS(vp);
time_t attrtimeo;
struct timespec ts;
uint32_t iosize;
SMB_CACHE_TIME(ts, np, attrtimeo);
if (useCacheDataOnly) {
} else if (np->n_flag & NMARKEDFORDLETE) {
}
else if ((ts.tv_sec - np->attribute_cache_timer) > attrtimeo)
return (ENOENT);
if (!va)
return (0);
VATTR_RETURN(va, va_rdev, 0);
if ((UNIX_CAPS(share) & UNIX_QFILEINFO_UNIX_INFO2_CAP))
VATTR_RETURN(va, va_nlink, np->n_nlinks);
else
VATTR_RETURN(va, va_nlink, 1);
if ((share->ss_attributes & FILE_NAMED_STREAMS) &&
(VATTR_IS_ACTIVE(va, va_total_size))) {
if (vnode_isdir(vp)) {
VATTR_RETURN(va, va_total_size, np->n_size);
lck_mtx_lock(&smp->sm_statfslock);
if (smp->sm_statfsbuf.f_bsize)
VATTR_RETURN(va, va_total_alloc, roundup(va->va_total_size,
smp->sm_statfsbuf.f_bsize));
lck_mtx_unlock(&smp->sm_statfslock);
}
else if (!vnode_isnamedstream(vp)) {
if (!useCacheDataOnly) {
(void)smb_get_rsrcfrk_size(share, vp, context);
}
lck_mtx_lock(&np->rfrkMetaLock);
VATTR_RETURN(va, va_total_size, np->n_size + np->rfrk_size);
lck_mtx_unlock(&np->rfrkMetaLock);
lck_mtx_lock(&smp->sm_statfslock);
if (smp->sm_statfsbuf.f_bsize)
VATTR_RETURN(va, va_total_alloc, roundup(va->va_total_size,
smp->sm_statfsbuf.f_bsize));
lck_mtx_unlock(&smp->sm_statfslock);
}
}
VATTR_RETURN(va, va_data_size, np->n_size);
VATTR_RETURN(va, va_data_alloc, np->n_data_alloc);
iosize = MIN(SSTOVC(share)->vc_rxmax, SSTOVC(share)->vc_wxmax);
VATTR_RETURN(va, va_iosize, iosize);
if (VATTR_IS_ACTIVE(va, va_mode))
VATTR_RETURN(va, va_mode, np->n_mode);
if (VATTR_IS_ACTIVE(va, va_uid) || VATTR_IS_ACTIVE(va, va_gid)) {
if (SMBV_HAS_GUEST_ACCESS(SSTOVC(share))) {
VATTR_RETURN(va, va_uid, UNKNOWNUID);
VATTR_RETURN(va, va_gid, UNKNOWNGID);
} else {
if (np->n_uid == KAUTH_UID_NONE)
VATTR_RETURN(va, va_uid, smp->sm_args.uid);
else
VATTR_RETURN(va, va_uid, np->n_uid);
if (np->n_gid == KAUTH_GID_NONE)
VATTR_RETURN(va, va_gid, smp->sm_args.gid);
else
VATTR_RETURN(va, va_gid, np->n_gid);
}
}
if (VATTR_IS_ACTIVE(va, va_flags)) {
va->va_flags = 0;
if (!vnode_isdir(vp) && !(np->n_dosattr & SMB_EFA_ARCHIVE))
va->va_flags |= SF_ARCHIVED;
if (node_isimmutable(share, vp)) {
va->va_flags |= UF_IMMUTABLE;
}
if ((np->n_dosattr & SMB_EFA_HIDDEN) &&
(!vnode_isvroot(vp) || (vfs_flags(smp->sm_mp) & MNT_DONTBROWSE))) {
va->va_flags |= UF_HIDDEN;
}
VATTR_SET_SUPPORTED(va, va_flags);
}
VATTR_RETURN(va, va_create_time, np->n_crtime);
VATTR_RETURN(va, va_modify_time, np->n_mtime);
VATTR_RETURN(va, va_access_time, np->n_atime);
if (share->ss_fstype == SMB_FS_FAT)
np->n_chtime.tv_sec = np->n_mtime.tv_sec;
VATTR_RETURN(va, va_change_time, np->n_chtime);
VATTR_RETURN(va, va_fileid, np->n_ino ? np->n_ino : 2);
VATTR_RETURN(va, va_linkid, np->n_ino ? np->n_ino : 2);
VATTR_RETURN(va, va_fsid, vfs_statfs(vnode_mount(vp))->f_fsid.val[0]);
VATTR_RETURN(va, va_filerev, 0);
VATTR_RETURN(va, va_gen, 0);
if (VATTR_IS_ACTIVE(va, va_name) && !vnode_isvroot(vp)) {
strlcpy ((char*) va->va_name, (char*)np->n_name, MAXPATHLEN);
VATTR_SET_SUPPORTED(va, va_name);
}
return (0);
}
void
smbfs_attr_touchdir(struct smbnode *dnp, int fatShare)
{
if (fatShare) {
struct timespec ts, ta;
ta.tv_sec = 1;
ta.tv_nsec = 0;
timespecadd(&dnp->n_mtime, &ta);
nanotime(&ts);
if (timespeccmp(&dnp->n_mtime, &ts, <))
dnp->n_mtime = ts;
}
dnp->attribute_cache_timer = 0;
}
int
smbfsIsCacheable(vnode_t vp)
{
if (!vnode_isreg(vp)) {
return FALSE;
}
if (vnode_isnocache(vp)) {
return FALSE;
} else {
return TRUE;
}
}
void
smbfs_setsize(vnode_t vp, off_t size)
{
struct smbnode *np = VTOSMB(vp);
np->n_size = size;
ubc_setsize(vp, size);
nanouptime(&np->n_sizetime);
VTOSMBFS(vp)->sm_statfstime = 0;
}
int
smbfs_update_size(struct smbnode *np, struct timespec *reqtime, u_quad_t new_size)
{
if (np->n_size == new_size)
return TRUE;
if (np->n_flag & NNEEDS_EOF_SET) {
SMB_LOG_IO("%s: Waiting on pending seteof, old eof = %lld new eof = %lld\n",
np->n_name, np->n_size, new_size);
return FALSE;
}
if (np->waitOnClusterWrite) {
SMB_LOG_IO("%s: Waiting on cluster write to complete, old eof = %lld new eof = %lld\n",
np->n_name, np->n_size, new_size);
return FALSE;
}
if (timespeccmp(reqtime, &np->n_sizetime, <=)) {
SMB_LOG_IO("%s: We set the eof after this lookup, old eof = %lld new eof = %lld\n",
np->n_name, np->n_size, new_size);
return FALSE;
}
ubc_msync (np->n_vnode, 0, ubc_getsize(np->n_vnode), NULL, UBC_PUSHDIRTY | UBC_SYNC);
SMBDEBUG("%s: smbfs_setsize, old eof = %lld new eof = %lld time %ld:%ld %ld:%ld\n",
np->n_name, np->n_size, new_size,
np->n_sizetime.tv_sec, np->n_sizetime.tv_nsec,
reqtime->tv_sec, reqtime->tv_nsec);
smbfs_setsize(np->n_vnode, new_size);
return TRUE;
}
int
FindByteRangeLockEntry(struct fileRefEntry *fndEntry, int64_t offset,
int64_t length, uint32_t lck_pid)
{
struct ByteRangeLockEntry *curr = fndEntry->lockList;
while (curr) {
if ((curr->offset == offset) && (curr->length == length) &&
(curr->lck_pid == lck_pid))
return TRUE;
curr = curr->next;
}
return FALSE;
}
void
AddRemoveByteRangeLockEntry(struct fileRefEntry *fndEntry, int64_t offset,
int64_t length, int8_t unLock, uint32_t lck_pid)
{
struct ByteRangeLockEntry *curr = NULL;
struct ByteRangeLockEntry *prev = NULL;
struct ByteRangeLockEntry *new = NULL;
int32_t foundIt = 0;
if (unLock == 0) {
SMB_MALLOC (new, struct ByteRangeLockEntry *, sizeof (struct ByteRangeLockEntry),
M_TEMP, M_WAITOK);
new->offset = offset;
new->length = length;
new->lck_pid = lck_pid;
new->next = NULL;
curr = fndEntry->lockList;
if (curr == NULL) {
fndEntry->lockList = new;
} else {
while (curr->next != NULL)
curr = curr->next;
curr->next = new;
}
} else {
curr = fndEntry->lockList;
if (curr == NULL) {
SMBWARNING("AddRemoveByteRangeLockEntry: no entries found\n");
return;
}
if ((curr->offset == offset) && (curr->length == length)) {
fndEntry->lockList = curr->next;
SMB_FREE(curr, M_TEMP);
} else {
prev = curr;
curr = curr->next;
while (curr != NULL) {
if ((curr->offset == offset) && (curr->length == length)) {
foundIt = 1;
prev->next = curr->next;
SMB_FREE(curr, M_TEMP);
break;
}
prev = curr;
curr = curr->next;
}
if (foundIt == 0) {
SMBWARNING ("offset 0x%llx/0x%llx not found in fndEntry %p\n",
offset, length, (void *)fndEntry);
}
}
}
}
void
AddFileRef(vnode_t vp, struct proc *p, uint16_t accessMode, uint32_t rights,
uint16_t fid, struct fileRefEntry **fndEntry)
{
struct smbnode *np = VTOSMB(vp);
struct fileRefEntry *entry = NULL;
struct fileRefEntry *current = NULL;
SMB_MALLOC(entry, struct fileRefEntry *, sizeof (struct fileRefEntry), M_TEMP,
M_WAITOK);
entry->refcnt = 0;
entry->mmapped = FALSE;
entry->proc = p;
entry->p_pid = proc_pid(p);
entry->accessMode = accessMode;
entry->rights = rights;
entry->fid = fid;
entry->lockList = NULL;
entry->next = NULL;
if (fndEntry) *fndEntry = entry;
lck_mtx_lock(&np->f_openDenyListLock);
if (np->f_openDenyList == NULL) {
np->f_openDenyList = entry;
}
else {
current = np->f_openDenyList;
while (current->next != NULL) {
current = current->next;
}
current->next = entry;
}
lck_mtx_unlock(&np->f_openDenyListLock);
}
int32_t
FindFileEntryByFID(vnode_t vp, uint16_t fid, struct fileRefEntry **fndEntry)
{
struct fileRefEntry *entry = NULL;
struct smbnode *np;
#ifdef SMB_DEBUG
if (fndEntry)
DBG_ASSERT(*fndEntry == NULL);
#endif // SMB_DEBUG
if (!vp) {
return (-1);
}
np = VTOSMB(vp);
lck_mtx_lock(&np->f_openDenyListLock);
for (entry = np->f_openDenyList; entry; entry = entry->next) {
if (entry->fid == fid) {
if (fndEntry) {
*fndEntry = entry;
}
lck_mtx_unlock(&np->f_openDenyListLock);
return(0);
}
}
lck_mtx_unlock(&np->f_openDenyListLock);
return(-1);
}
int32_t
FindMappedFileRef(vnode_t vp, struct fileRefEntry **fndEntry, uint16_t *fid)
{
struct fileRefEntry *entry = NULL;
int32_t foundIt = FALSE;
struct smbnode *np;
if (!vp) {
return (foundIt);
}
np = VTOSMB(vp);
lck_mtx_lock(&np->f_openDenyListLock);
for (entry = np->f_openDenyList; entry; entry = entry->next) {
if (entry->mmapped) {
if (fid) {
*fid = entry->fid;
}
if (fndEntry) {
*fndEntry = entry;
}
foundIt = TRUE;
break;
}
}
lck_mtx_unlock(&np->f_openDenyListLock);
return (foundIt);
}
int32_t
FindFileRef(vnode_t vp, proc_t p, uint16_t accessMode, int32_t flags,
int64_t offset, int64_t length, struct fileRefEntry **fndEntry,
uint16_t *fid)
{
struct fileRefEntry *entry = NULL;
struct fileRefEntry *tempEntry = NULL;
struct ByteRangeLockEntry *currBRL = NULL;
int32_t foundIt = 0;
struct smbnode *np;
#ifdef SMB_DEBUG
if (fndEntry)
DBG_ASSERT(*fndEntry == NULL);
#endif // SMB_DEBUG
if (!vp) {
return (-1);
}
np = VTOSMB(vp);
lck_mtx_lock(&np->f_openDenyListLock);
for (entry = np->f_openDenyList; entry; entry = entry->next) {
if ((p) && (entry->p_pid != proc_pid(p))) {
SMBERROR("pid not matching \n");
continue;
}
switch (flags) {
case kAnyMatch:
if (entry->accessMode & accessMode) {
foundIt = 1;
}
break;
case kCheckDenyOrLocks:
if (entry->accessMode & accessMode) {
if (tempEntry == NULL) {
tempEntry = entry;
}
currBRL = entry->lockList;
while (currBRL != NULL) {
if ( (offset >= currBRL->offset) &&
(offset <= (currBRL->offset + currBRL->length)) ) {
foundIt = 1;
break;
}
if ( ((offset + length) >= currBRL->offset) &&
((offset + length) <= (currBRL->offset + currBRL->length)) ) {
foundIt = 1;
break;
}
currBRL = currBRL->next;
}
}
break;
case kExactMatch:
default:
if (accessMode == entry->accessMode)
foundIt = 1;
break;
}
if (foundIt == 1) {
*fid = entry->fid;
if (fndEntry) {
*fndEntry = entry;
}
break;
}
}
lck_mtx_unlock(&np->f_openDenyListLock);
if (foundIt == 0) {
if ( (flags == kCheckDenyOrLocks) && (tempEntry != NULL) ) {
*fid = tempEntry->fid;
if (fndEntry) {
*fndEntry = entry;
}
return (0);
}
return (-1);
}
else
return (0);
}
void
RemoveFileRef(vnode_t vp, struct fileRefEntry *inEntry)
{
struct smbnode *np = VTOSMB(vp);
struct fileRefEntry *curr = NULL;
struct fileRefEntry *prev = NULL;
struct fileRefEntry *entry = NULL;
struct ByteRangeLockEntry *currBRL = NULL;
struct ByteRangeLockEntry *nextBRL = NULL;
int32_t foundIt = 0;
lck_mtx_lock(&np->f_openDenyListLock);
if (inEntry == NULL) {
entry = np->f_openDenyList;
while (entry != NULL) {
currBRL = entry->lockList;
while (currBRL != NULL) {
nextBRL = currBRL->next;
SMB_FREE (currBRL, M_TEMP);
currBRL = nextBRL;
}
entry->lockList = NULL;
curr = entry;
entry = entry->next;
DBG_ASSERT(curr->refcnt == 0);
SMB_FREE(curr, M_TEMP);
}
np->f_openDenyList = NULL;
goto out;
}
DBG_ASSERT(inEntry->refcnt == 0);
currBRL = inEntry->lockList;
while (currBRL != NULL) {
nextBRL = currBRL->next;
SMB_FREE(currBRL, M_TEMP);
currBRL = nextBRL;
}
inEntry->lockList = NULL;
curr = np->f_openDenyList;
if (curr == NULL)
goto out;
if (inEntry == curr) {
np->f_openDenyList = inEntry->next;
foundIt = 1;
SMB_FREE(curr, M_TEMP);
curr = NULL;
} else {
prev = np->f_openDenyList;
curr = prev->next;
while (curr != NULL) {
if (inEntry == curr) {
prev->next = curr->next;
foundIt = 1;
SMB_FREE(curr, M_TEMP);
curr = NULL;
break;
}
prev = curr;
curr = curr->next;
}
}
if (foundIt == 0)
SMBWARNING ("inEntry %p not found in vp %p\n", (void *)inEntry, (void *)vp);
out:
lck_mtx_unlock(&np->f_openDenyListLock);
}
void
smbfs_reconnect(struct smbmount *smp)
{
struct smbnode *np;
uint32_t ii;
smbfs_hash_lock(smp);
for (ii = 0; ii < (smp->sm_hashlen + 1); ii++) {
if ((&smp->sm_hash[ii])->lh_first == NULL)
continue;
for (np = (&smp->sm_hash[ii])->lh_first; np; np = np->n_hash.le_next) {
if (ISSET(np->n_flag, NALLOC))
continue;
if (ISSET(np->n_flag, NTRANSIT))
continue;
if (np->n_vnode && (vnode_ismonitored(np->n_vnode))) {
SMBDEBUG("%s needs to be updated.\n", np->n_name);
if ((np->n_dosattr & SMB_EFA_DIRECTORY) && np->d_fid)
np->d_needReopen = TRUE;
np->attribute_cache_timer = 0;
np->n_symlink_cache_timer = 0;
np->d_needsUpdate = TRUE;
}
if (np->n_dosattr & SMB_EFA_DIRECTORY) {
continue;
}
if (np->f_refcnt == 0) {
continue;
}
lck_mtx_lock(&np->f_openStateLock);
if (np->f_openState != kNeedRevoke) {
if (np->f_openDenyList) {
np->f_openState = kNeedRevoke;
} else {
np->f_openState = kNeedReopen;
}
}
lck_mtx_unlock(&np->f_openStateLock);
}
}
smbfs_hash_unlock(smp);
}
int32_t
smbfs_IObusy(struct smbmount *smp)
{
struct smbnode *np;
uint32_t ii;
smbfs_hash_lock(smp);
for (ii = 0; ii < (smp->sm_hashlen + 1); ii++) {
if ((&smp->sm_hash[ii])->lh_first == NULL)
continue;
for (np = (&smp->sm_hash[ii])->lh_first; np; np = np->n_hash.le_next) {
if (ISSET(np->n_flag, NALLOC))
continue;
if (ISSET(np->n_flag, NTRANSIT))
continue;
if (np->n_dosattr & SMB_EFA_DIRECTORY) {
continue;
}
if (np->f_refcnt == 0) {
continue;
}
if ((np->f_openTotalWCnt > 0) || (vnode_hasdirtyblks(SMBTOV(np)))) {
smbfs_hash_unlock(smp);
return EBUSY;
}
}
}
smbfs_hash_unlock(smp);
return 0;
}
void
smbfs_ClearChildren(struct smbmount *smp, struct smbnode * parent)
{
struct smbnode *np;
uint32_t ii;
smbfs_hash_lock(smp);
for (ii = 0; ii < (smp->sm_hashlen + 1); ii++) {
if ((&smp->sm_hash[ii])->lh_first == NULL)
continue;
for (np = (&smp->sm_hash[ii])->lh_first; np; np = np->n_hash.le_next) {
if (ISSET(np->n_flag, NALLOC))
continue;
if (ISSET(np->n_flag, NTRANSIT))
continue;
if (np->n_parent == parent) {
np->n_flag &= ~NREFPARENT;
}
}
}
smbfs_hash_unlock(smp);
}