#include <stdint.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/vnode.h>
#include <sys/xattr.h>
#include <sys/kpi_mbuf.h>
#include <sys/mount.h>
#include <sys/kauth.h>
#include <sys/smb_apple.h>
#include <sys/msfscc.h>
#include <netsmb/smb.h>
#include <netsmb/smb_2.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_rq_2.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_conn_2.h>
#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
#include <smbfs/smbfs_subr_2.h>
#include <smbfs/smbfs_lockf.h>
#include <netsmb/smb_converter.h>
#include <smbfs/smbfs_security.h>
#include <smbclient/ntstatus.h>
#include <libkern/crypto/md5.h>
#define SMBFS_DELETE_PREFIX ".smbdeleteAAA"
#define MAKE_DELETED_NAME(NAME, SIZE, FID) \
(void) snprintf((NAME), SIZE, "%s%llx", SMBFS_DELETE_PREFIX, (FID))
uint64_t
smbfs_getino(struct smbnode *dnp, const char *name, size_t nmlen)
{
uint64_t ino;
ino = dnp->n_ino + smbfs_hash(NULL, 0, name, nmlen);
if (ino <= SMBFS_ROOT_INO)
ino += 3;
return ino;
}
int
smb1fs_smb_lock(struct smb_share *share, int op, SMBFID fid, uint32_t pid,
off_t start, uint64_t len, uint32_t timo, vfs_context_t context)
{
#pragma unused(pid)
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
u_char ltype = 0;
int error;
uint16_t smb1_fid = (uint16_t) fid;
if (op == SMB_LOCK_SHARED)
ltype |= SMB_LOCKING_ANDX_SHARED_LOCK;
if (VC_CAPS(SSTOVC(share)) & SMB_CAP_LARGE_FILES)
ltype |= SMB_LOCKING_ANDX_LARGE_FILES;
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_LOCKING_ANDX, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
mb_put_uint8(mbp, ltype);
mb_put_uint8(mbp, 0);
mb_put_uint32le(mbp, timo);
mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0);
mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint16le(mbp, rqp->sr_pidLow);
if (ltype & SMB_LOCKING_ANDX_LARGE_FILES) {
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, (uint32_t)(start >> 32));
mb_put_uint32le(mbp, (uint32_t)(start & 0xffffffff));
mb_put_uint32le(mbp, (uint32_t)(len >> 32));
mb_put_uint32le(mbp, (uint32_t)(len & 0xffffffff));
} else {
mb_put_uint32le(mbp, (uint32_t)(start & 0xffffffff));
mb_put_uint32le(mbp, (uint32_t)(len & 0xffffffff));
}
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if ((error == EIO) && (rqp->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS)) {
if (rqp->sr_ntstatus == STATUS_FILE_LOCK_CONFLICT)
error = EACCES;
}
smb_rq_done(rqp);
return error;
}
int
smb1fs_smb_qpathinfo(struct smb_share *share,
struct smbnode *np,
struct smbfattr *fap,
short infolevel,
const char **namep,
size_t *nmlenp,
vfs_context_t context)
{
struct smb_t2rq *t2p;
int error;
struct mbchain *mbp;
struct mdchain *mdp;
uint64_t llint;
uint32_t size, dattr, eaSize;
const char *name = (namep ? *namep : NULL);
size_t nmlen = (nmlenp ? *nmlenp : 0);
char *ntwrkname = NULL;
char *filename;
uint8_t sep = '\\';
char *snamep = NULL;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_QUERY_PATH_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, infolevel);
mb_put_uint32le(mbp, 0);
if ((np->n_vnode) && vnode_isnamedstream(np->n_vnode)) {
DBG_ASSERT((namep == NULL));
lck_rw_lock_shared(&np->n_name_rwlock);
snamep = smb_strndup(np->n_sname, np->n_snmlen);
name = snamep;
lck_rw_unlock_shared(&np->n_name_rwlock);
nmlen = np->n_snmlen;
sep = ':';
}
error = smbfs_fullpath(mbp, np, name, &nmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), sep);
if (error) {
smb_t2_done(t2p);
goto done;
}
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = SSTOVC(share)->vc_txmax;
error = smb_t2_request(t2p);
if (error) {
smb_t2_done(t2p);
goto done;
}
mdp = &t2p->t2_rdata;
if (mdp->md_cur == NULL) {
SMBWARNING("Parsing error reading the message\n");
smb_t2_done(t2p);
error = EBADRPC;
goto done;
}
switch (infolevel) {
case SMB_QFILEINFO_ALL_INFO:
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &fap->fa_crtime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &fap->fa_atime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &fap->fa_mtime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &fap->fa_chtime);
}
md_get_uint32le(mdp, &dattr);
fap->fa_attr = dattr;
if (fap->fa_attr & SMB_EFA_DIRECTORY) {
fap->fa_valid_mask |= FA_VTYPE_VALID;
}
fap->fa_vtype = (fap->fa_attr & SMB_EFA_DIRECTORY) ? VDIR : VREG;
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, &llint);
fap->fa_data_alloc = llint;
md_get_uint64le(mdp, &llint);
fap->fa_size = llint;
md_get_uint32le(mdp, NULL);
md_get_uint8(mdp, NULL);
error = md_get_uint8(mdp, NULL);
if (error)
goto bad;
fap->fa_ino = np->n_ino;
md_get_uint16(mdp, NULL);
md_get_uint32le(mdp, &eaSize);
if (namep == NULL)
break;
error = md_get_uint32le(mdp, &size);
if (error)
goto bad;
if (size <= 0 || size >= t2p->t2_maxdcount) {
error = EINVAL;
goto bad;
}
m_fixhdr(mdp->md_top);
if (mbuf_pkthdr_len(mdp->md_top) > (size+72+4)) {
SMBERROR("SMB_QFILEINFO_ALL_INFO: wrong size %ld\n",
mbuf_pkthdr_len((mbuf_t)mdp->md_top));
}
nmlen = size;
SMB_MALLOC(ntwrkname, char *, nmlen, M_SMBFSDATA, M_WAITOK);
if (ntwrkname == NULL)
error = ENOMEM;
else
error = md_get_mem(mdp, (void *)ntwrkname, nmlen,
MB_MSYSTEM);
if (error)
goto bad;
if (SMB_UNICODE_STRINGS(SSTOVC(share))) {
if (nmlen > 1 && ntwrkname[nmlen - 1] == 0 &&
ntwrkname[nmlen - 2] == 0)
nmlen -= 2;
if (nmlen > 1)
filename = &ntwrkname[nmlen-2];
else filename = ntwrkname;
while (filename > ntwrkname) {
if ((*filename == 0x5c) && (*(filename+1) == 0x00))
break;
filename -= 2;
}
if ((*filename == 0x5c) && (*(filename+1) == 0x00))
filename += 2;
} else {
if (nmlen && ntwrkname[nmlen - 1] == 0)
nmlen--;
if (nmlen)
filename = &ntwrkname[nmlen-1];
else filename = ntwrkname;
while ((filename > ntwrkname) && (*filename != 0x5c))
filename--;
if (*filename == 0x5c)
filename++;
}
nmlen = &ntwrkname[nmlen] - filename;
filename = smbfs_ntwrkname_tolocal((const char *)filename, &nmlen,
SMB_UNICODE_STRINGS(SSTOVC(share)));
SMB_FREE(ntwrkname, M_SMBFSDATA);
ntwrkname = filename;
if (ntwrkname == NULL) {
error = EINVAL;
SMBERROR("smbfs_ntwrkname_tolocal return NULL\n");
goto bad;
}
if (nmlen > SMB_MAXFNAMELEN) {
error = EINVAL;
SMBERROR("Filename %s nmlen = %ld\n", ntwrkname, nmlen);
goto bad;
}
*namep = smb_strndup(filename, nmlen);
if (nmlenp)
*nmlenp = nmlen;
if (*namep && nmlenp)
fap->fa_ino = smbfs_getino(np, *namep, *nmlenp);
bad:
SMB_FREE(ntwrkname, M_SMBFSDATA);
break;
case SMB_QFILEINFO_UNIX_INFO2:
md_get_uint64le(mdp, &llint);
fap->fa_size = llint;
md_get_uint64le(mdp, &llint);
fap->fa_data_alloc = llint;
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, &fap->fa_chtime);
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, &fap->fa_atime);
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, &fap->fa_mtime);
md_get_uint64le(mdp, &llint);
fap->fa_uid = llint;
md_get_uint64le(mdp, &llint);
fap->fa_gid = llint;
md_get_uint32le(mdp, &dattr);
fap->fa_valid_mask |= FA_VTYPE_VALID;
if (dattr & EXT_UNIX_DIR) {
fap->fa_attr |= SMB_EFA_DIRECTORY;
fap->fa_vtype = VDIR;
} else if (dattr & EXT_UNIX_SYMLINK) {
fap->fa_vtype = VLNK;
} else {
fap->fa_vtype = VREG;
}
md_get_uint64le(mdp, &llint);
md_get_uint64le(mdp, &llint);
md_get_uint64le(mdp, &llint);
md_get_uint64le(mdp, &llint);
fap->fa_permissions = llint;
fap->fa_valid_mask |= FA_UNIX_MODES_VALID;
md_get_uint64le(mdp, &llint);
fap->fa_nlinks = llint;
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, &fap->fa_crtime);
md_get_uint32le(mdp, &dattr);
error = md_get_uint32le(mdp, &fap->fa_flags_mask);
if (error)
break;
if (fap->fa_flags_mask & EXT_HIDDEN) {
if (dattr & EXT_HIDDEN)
fap->fa_attr |= SMB_EFA_HIDDEN;
else
fap->fa_attr &= ~SMB_EFA_HIDDEN;
}
if (fap->fa_flags_mask & EXT_IMMUTABLE) {
if (dattr & EXT_IMMUTABLE)
fap->fa_attr |= SMB_EFA_RDONLY;
else
fap->fa_attr &= ~SMB_EFA_RDONLY;
}
if (fap->fa_flags_mask & SMB_EFA_ARCHIVE) {
if (dattr & EXT_DO_NOT_BACKUP)
fap->fa_attr &= ~SMB_EFA_ARCHIVE;
else
fap->fa_attr |= SMB_EFA_ARCHIVE;
}
fap->fa_unix = TRUE;
if (namep == NULL) {
fap->fa_ino = np->n_ino;
}
else {
if ((*namep) && nmlenp) {
fap->fa_ino = smbfs_getino(np, *namep, *nmlenp);
}
else {
SMBERROR("Missing namep or nmlenp, so inode left at 0\n");
}
}
break;
default:
SMBERROR("unexpected info level %d\n", infolevel);
error = EINVAL;
break;
}
smb_t2_done(t2p);
done:
if (snamep) {
SMB_FREE(snamep, M_SMBSTR);
}
return error;
}
int
smbfs_smb_undollardata(const char *fname, char *name, size_t *nmlen,
uint32_t *is_data)
{
char *cp;
size_t len = sizeof(SMB_DATASTREAM) - 1;
*is_data = 0;
if (!name) {
goto bad;
}
if (*nmlen < len + 1) {
goto bad;
}
if (*name != ':') {
goto bad;
}
cp = &name[*nmlen - len];
if (bcmp(cp, SMB_DATASTREAM, len)) {
goto bad;
}
if (*nmlen == (len + 1)) {
*is_data = 1;
return (0);
}
if ((*nmlen - len) > (XATTR_MAXNAMELEN + 1)) {
goto bad;
}
*nmlen -= len;
*cp = '\0';
if ((*nmlen >= 17) && (xattr_protected(name + 1))) {
return (0);
}
return (1);
bad:
SMBWARNING("file \"%s\" has bad stream \"%s\"\n", fname, (name) ? name : "");
return (0);
}
int
smb1fs_smb_markfordelete(struct smb_share *share, SMBFID fid,
vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
int error;
uint16_t smb1_fid = (uint16_t) fid;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_FILE_INFORMATION, 1,
context, &t2p);
if (error) {
return error;
}
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
if (VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU)
mb_put_uint16le(mbp, SMB_SFILEINFO_DISPOSITION_INFORMATION);
else
mb_put_uint16le(mbp, SMB_SFILEINFO_DISPOSITION_INFO);
mb_put_uint16le(mbp, 0);
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_uint8(mbp, 1);
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = 0;
error = smb_t2_request(t2p);
smb_t2_done(t2p);
return error;
}
static void *
smbfs_create_windows_symlink_data(const char *target, size_t targetlen,
uint32_t *rtlen)
{
MD5_CTX md5;
uint32_t state[4];
uint32_t datalen, filelen;
char *wbuf, *wp;
int maxwplen;
uint32_t targlen = (uint32_t)targetlen;
datalen = SMB_SYMHDRLEN + targlen;
filelen = SMB_SYMLEN;
maxwplen = filelen;
SMB_MALLOC(wbuf, void *, filelen, M_TEMP, M_WAITOK);
wp = wbuf;
bcopy(smb_symmagic, wp, SMB_SYMMAGICLEN);
wp += SMB_SYMMAGICLEN;
maxwplen -= SMB_SYMMAGICLEN;
(void)snprintf(wp, maxwplen, "%04d\n", targlen);
wp += SMB_SYMLENLEN;
maxwplen -= SMB_SYMLENLEN;
MD5Init(&md5);
MD5Update(&md5, (unsigned char *)target, targlen);
MD5Final((u_char *)state, &md5);
(void)snprintf(wp, maxwplen, "%08x%08x%08x%08x\n", htobel(state[0]),
htobel(state[1]), htobel(state[2]), htobel(state[3]));
wp += SMB_SYMMD5LEN;
bcopy(target, wp, targlen);
wp += targlen;
if (datalen < filelen) {
*wp++ = '\n';
datalen++;
if (datalen < filelen)
memset((caddr_t)wp, ' ', filelen - datalen);
}
*rtlen = filelen;
return wbuf;
}
static int
smb_setfile_unix_symlink(struct smb_share *share, struct smbnode *dnp,
const char *name, size_t nmlen, char *target,
size_t targetlen, vfs_context_t context)
{
struct smb_t2rq *t2p = NULL;
struct mbchain *mbp;
int error;
char *ntwrkpath = NULL;
size_t ntwrkpathlen = targetlen * 2;
SMB_MALLOC(ntwrkpath, char *, ntwrkpathlen, M_SMBFSDATA, M_WAITOK | M_ZERO);
error = smb_convert_path_to_network(target, targetlen, ntwrkpath,
&ntwrkpathlen, '/', NO_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)));
if (! error) {
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_PATH_INFORMATION, 1,
context, &t2p);
}
if (error) {
goto done;
}
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, SMB_SFILEINFO_UNIX_LINK);
mb_put_uint32le(mbp, 0);
error = smbfs_fullpath(mbp, dnp, name, &nmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error) {
goto done;
}
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)(ntwrkpath), ntwrkpathlen, MB_MSYSTEM);
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = SSTOVC(share)->vc_txmax;
error = smb_t2_request(t2p);
done:
SMB_FREE(ntwrkpath, M_SMBFSDATA);
if (t2p) {
smb_t2_done(t2p);
}
return error;
}
static int
smbfs_smb_fsctl(struct smb_share *share, uint32_t fsctl, SMBFID fid,
uint32_t datacnt, struct mbchain *mbp, struct mdchain *mdp,
Boolean * moreDataRequired, vfs_context_t context)
{
struct smb_ntrq *ntp = NULL;
int error;
uint16_t smb1_fid = (uint16_t) fid;
error = smb_nt_alloc(SSTOCP(share), NT_TRANSACT_IOCTL, context, &ntp);
if (error) {
goto done;
}
ntp->nt_maxdcount = datacnt;
mb_init(&ntp->nt_tsetup);
mb_put_uint32le(&ntp->nt_tsetup, fsctl);
mb_put_uint16le(&ntp->nt_tsetup, smb1_fid);
mb_put_uint8(&ntp->nt_tsetup, 1);
mb_put_uint8(&ntp->nt_tsetup, 0);
if (mbp) {
ntp->nt_tdata = *mbp;
memset(mbp, 0, sizeof(*mbp));
}
error = smb_nt_request(ntp);
if (error) {
if (moreDataRequired && (ntp->nt_flags & SMBT2_MOREDATA)) {
*moreDataRequired = TRUE;
}
goto done;
}
if (ntp->nt_rdata.md_top && mdp) {
SMBSYMDEBUG("Repase data size = %d\n", (int)datalen);
md_initm(mdp, ntp->nt_rdata.md_top);
memset(&ntp->nt_rdata, 0, sizeof(ntp->nt_rdata));
}
done:
if (ntp) {
smb_nt_done(ntp);
}
return error;
}
void
smbfs_update_symlink_cache(struct smbnode *np, char *target, size_t targetlen)
{
struct timespec ts;
if (np->n_symlink_target != NULL) {
SMB_FREE(np->n_symlink_target, M_TEMP);
}
np->n_symlink_target_len = 0;
np->n_symlink_target = smb_strndup(target, targetlen);
if (np->n_symlink_target) {
np->n_symlink_target_len = targetlen;
nanouptime(&ts);
np->n_symlink_cache_timer = ts.tv_sec;
}
}
int
smbfs_smb_create_unix_symlink(struct smb_share *share, struct smbnode *dnp,
const char *in_name, size_t in_nmlen, char *target,
size_t targetlen, struct smbfattr *fap,
vfs_context_t context)
{
const char *name = in_name;
size_t nmlen = in_nmlen;
int error;
memset(fap, 0, sizeof(*fap));
error = smb_setfile_unix_symlink(share, dnp, name, nmlen, target,
targetlen, context);
if (error) {
goto done;
}
error = smbfs_lookup(share, dnp, &name, &nmlen, fap, context);
if (error) {
struct smbmount *smp = dnp->n_mount;
fap->fa_attr = SMB_EFA_NORMAL;
fap->fa_size = targetlen;
fap->fa_data_alloc = roundup(fap->fa_size, smp->sm_statfsbuf.f_bsize);
nanotime(&fap->fa_crtime);
fap->fa_atime = fap->fa_crtime;
fap->fa_chtime = fap->fa_crtime;
fap->fa_mtime = fap->fa_crtime;
fap->fa_ino = smbfs_getino(dnp, in_name, in_nmlen);
nanouptime(&fap->fa_reqtime);
fap->fa_valid_mask |= FA_VTYPE_VALID;
fap->fa_vtype = VLNK;
fap->fa_nlinks = 1;
fap->fa_flags_mask = EXT_REQUIRED_BY_MAC;
fap->fa_unix = TRUE;
error = 0;
}
done:
if (name != in_name) {
SMB_FREE(name, M_SMBNODENAME);
}
if (error) {
SMBWARNING("Creating symlink for %s failed! error = %d\n", name, error);
}
return error;
}
int
smbfs_smb_create_windows_symlink(struct smb_share *share, struct smbnode *dnp,
const char *name, size_t nmlen,
char *target, size_t targetlen,
struct smbfattr *fap, vfs_context_t context)
{
uint32_t wlen = 0;
uio_t uio = NULL;
char *wdata = NULL;
int error;
SMBFID fid = 0;
uint64_t create_flags = SMB2_CREATE_DO_CREATE | SMB2_CREATE_GET_MAX_ACCESS;
wdata = smbfs_create_windows_symlink_data(target, targetlen, &wlen);
if (!wdata) {
error = ENOMEM;
goto done;
}
uio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
if (uio == NULL) {
error = ENOMEM;
goto done;
}
uio_addiov(uio, CAST_USER_ADDR_T(wdata), wlen);
if (SSTOVC(share)->vc_flags & SMBV_SMB2) {
error = smb2fs_smb_cmpd_create_write(share, dnp,
name, nmlen,
NULL, 0,
SMB2_FILE_WRITE_DATA, fap,
create_flags, uio,
NULL, 0,
context);
}
else {
error = smbfs_smb_create(share, dnp, name, nmlen, SMB2_FILE_WRITE_DATA,
&fid, FILE_CREATE, 0, fap, context);
if (error) {
goto done;
}
error = smb_smb_write(share, fid, uio, 0, context);
(void) smbfs_smb_close(share, fid, context);
}
if (!error) {
fap->fa_size = wlen;
}
done:
if (uio != NULL) {
uio_free(uio);
}
if (wdata) {
SMB_FREE(wdata, M_TEMP);
}
if (error) {
SMBWARNING("Creating symlink for %s failed! error = %d\n", name, error);
}
return error;
}
int
smb1fs_smb_create_reparse_symlink(struct smb_share *share, struct smbnode *dnp,
const char *name, size_t nmlen, char *target,
size_t targetlen, struct smbfattr *fap,
vfs_context_t context)
{
struct smbmount *smp = dnp->n_mount;
int error;
SMBFID fid = 0;
struct mbchain mbp;
size_t path_len;
char *path;
uint16_t reparseLen;
uint16_t SubstituteNameOffset, SubstituteNameLength;
uint16_t PrintNameOffset, PrintNameLength;
int need_close = 0;
uint32_t rights = SMB2_FILE_WRITE_DATA | SMB2_FILE_WRITE_ATTRIBUTES | SMB2_DELETE;
error = smbfs_smb_ntcreatex(share, dnp,
rights, NTCREATEX_SHARE_ACCESS_ALL, VLNK,
&fid, name, nmlen,
FILE_CREATE, FALSE, fap,
TRUE, NULL, context);
if (error) {
goto done;
}
need_close = 1;
path_len = (targetlen * 2) + 2;
SMB_MALLOC(path, char *, path_len, M_TEMP, M_WAITOK | M_ZERO);
if (path == NULL) {
error = ENOMEM;
goto done;
}
error = smb_convert_path_to_network(target, targetlen, path, &path_len,
'\\', SMB_UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)));
if (error) {
SMB_FREE(path, M_TEMP);
goto done;
}
SubstituteNameLength = path_len;
SubstituteNameOffset = 0;
PrintNameOffset = SubstituteNameLength;
PrintNameLength = SubstituteNameLength;
reparseLen = SubstituteNameLength + PrintNameLength + 12;
mb_init(&mbp);
mb_put_uint32le(&mbp, IO_REPARSE_TAG_SYMLINK);
mb_put_uint16le(&mbp, reparseLen);
mb_put_uint16le(&mbp, 0);
mb_put_uint16le(&mbp, SubstituteNameOffset);
mb_put_uint16le(&mbp, SubstituteNameLength);
mb_put_uint16le(&mbp, PrintNameOffset);
mb_put_uint16le(&mbp, PrintNameLength);
if (*target == '/') {
mb_put_uint32le(&mbp, SYMLINK_FLAG_ABSOLUTE);
} else {
mb_put_uint32le(&mbp, SYMLINK_FLAG_RELATIVE);
}
mb_put_mem(&mbp, path, SubstituteNameLength, MB_MSYSTEM);
mb_put_mem(&mbp, path, PrintNameLength, MB_MSYSTEM);
SMB_FREE(path, M_TEMP);
error = smbfs_smb_fsctl(share, FSCTL_SET_REPARSE_POINT, fid, 0, &mbp,
NULL, NULL, context);
mb_done(&mbp);
if ((error == EACCES) && !(SSTOVC(share)->vc_flags & SMBV_DARWIN)) {
smp->sm_flags &= ~MNT_SUPPORTS_REPARSE_SYMLINKS;
}
if (error) {
(void)smbfs_smb_markfordelete(share, fid, context);
} else {
fap->fa_size = targetlen;
fap->fa_attr |= SMB_EFA_REPARSE_POINT;
fap->fa_valid_mask |= FA_REPARSE_TAG_VALID;
fap->fa_reparse_tag = IO_REPARSE_TAG_SYMLINK;
fap->fa_valid_mask |= FA_VTYPE_VALID;
fap->fa_vtype = VLNK;
}
done:
if (need_close) {
(void)smbfs_smb_close(share, fid, context);
}
if ((error == EACCES) && !(smp->sm_flags & MNT_SUPPORTS_REPARSE_SYMLINKS)) {
error = smbfs_smb_create_windows_symlink(share, dnp, name, nmlen, target,
targetlen, fap, context);
}
if (error) {
SMBWARNING("Creating symlink for %s failed! error = %d\n", name, error);
}
return error;
}
int
smbfs_smb_get_reparse_tag(struct smb_share *share, SMBFID fid,
uint32_t *reparseTag, char **outTarget,
size_t *outTargetlen, vfs_context_t context)
{
int error;
Boolean moreDataRequired = FALSE;
uint32_t rdatacnt = SSTOVC(share)->vc_txmax;
struct mdchain mdp;
uint16_t reparseLen = 0;
uint16_t SubstituteNameOffset = 0;
uint16_t SubstituteNameLength = 0;
uint16_t PrintNameOffset = 0;
uint16_t PrintNameLength = 0;
uint32_t Flags = 0;
char *ntwrkname = NULL;
char *target = NULL;
size_t targetlen;
memset(&mdp, 0, sizeof(mdp));
error = smbfs_smb_fsctl(share, FSCTL_GET_REPARSE_POINT, fid, rdatacnt, NULL,
&mdp, &moreDataRequired, context);
if (!error && !mdp.md_top) {
error = ENOENT;
}
if (error) {
goto done;
}
md_get_uint32le(&mdp, reparseTag);
if (*reparseTag != IO_REPARSE_TAG_SYMLINK) {
md_done(&mdp);
goto done;
}
md_get_uint16le(&mdp, &reparseLen);
md_get_uint16le(&mdp, NULL);
md_get_uint16le(&mdp, &SubstituteNameOffset);
md_get_uint16le(&mdp, &SubstituteNameLength);
md_get_uint16le(&mdp, &PrintNameOffset);
md_get_uint16le(&mdp, &PrintNameLength);
md_get_uint32le(&mdp, &Flags);
SMBSYMDEBUG("reparseLen = %d SubstituteNameOffset = %d SubstituteNameLength = %d PrintNameOffset = %d PrintNameLength = %d Flags = %d\n",
reparseLen,
SubstituteNameOffset, SubstituteNameLength,
PrintNameOffset, PrintNameLength, Flags);
if ((SubstituteNameLength == 0) || (SubstituteNameLength > SSTOVC(share)->vc_txmax)) {
error = ENOMEM;
SMBSYMDEBUG("SubstituteNameLength too large or zero %d \n", SubstituteNameLength);
md_done(&mdp);
goto done;
}
if (SubstituteNameOffset) {
md_get_mem(&mdp, NULL, SubstituteNameOffset, MB_MSYSTEM);
}
SMB_MALLOC(ntwrkname, char *, (size_t)SubstituteNameLength, M_TEMP, M_WAITOK | M_ZERO);
if (ntwrkname == NULL) {
error = ENOMEM;
} else {
error = md_get_mem(&mdp, (void *)ntwrkname, (size_t)SubstituteNameLength, MB_MSYSTEM);
}
md_done(&mdp);
if (error) {
goto done;
}
targetlen = SubstituteNameLength * 9 + 1;
SMB_MALLOC(target, char *, targetlen, M_TEMP, M_WAITOK | M_ZERO);
if (target == NULL) {
error = ENOMEM;
} else {
error = smb_convert_network_to_path(ntwrkname, SubstituteNameLength, target,
&targetlen, '\\', UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)));
}
if (!error) {
*outTarget = target;
*outTargetlen = targetlen;
target = NULL;
}
done:
SMB_FREE(ntwrkname, M_TEMP);
SMB_FREE(target, M_TEMP);
return error;
}
int
smb1fs_smb_reparse_read_symlink(struct smb_share *share, struct smbnode *np,
struct uio *uiop, vfs_context_t context)
{
int error;
uint32_t reparseTag = 0;
SMBFID fid = 0;
char *target = NULL;
size_t targetlen = 0;
int need_close = 0;
error = smbfs_tmpopen(share, np,
SMB2_FILE_READ_DATA | SMB2_FILE_READ_ATTRIBUTES,
&fid, context);
if (error) {
goto done;
}
need_close = 1;
error = smbfs_smb_get_reparse_tag(share, fid, &reparseTag, &target,
&targetlen, context);
if (!error && (reparseTag != IO_REPARSE_TAG_SYMLINK)) {
error = ENOENT;
} else if (!error && (target == NULL)) {
error = EINVAL;
}
if (error) {
goto done;
}
SMBSYMDEBUG_LOCK(np, "%s --> %s\n", np->n_name, target);
smbfs_update_symlink_cache(np, target, targetlen);
error = uiomove(target, (int)targetlen, uiop);
done:
SMB_FREE(target, M_TEMP);
if (need_close == 1) {
(void)smbfs_tmpclose(share, np, fid, context);
}
if (error) {
SMBWARNING_LOCK(np, "%s failed %d\n", np->n_name, error);
}
return error;
}
int
smbfs_smb_windows_read_symlink(struct smb_share *share, struct smbnode *np,
struct uio *uiop, vfs_context_t context)
{
unsigned char *wbuf, *cp;
unsigned len, flen;
uio_t uio;
int error;
SMBFID fid = 0;
char *target = NULL;
flen = SMB_SYMLEN;
SMB_MALLOC(wbuf, void *, flen, M_TEMP, M_WAITOK);
error = smbfs_tmpopen(share, np, SMB2_FILE_READ_DATA, &fid, context);
if (error)
goto out;
uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
uio_addiov(uio, CAST_USER_ADDR_T(wbuf), flen);
error = smb_smb_read(share, fid, uio, context);
uio_free(uio);
(void)smbfs_tmpclose(share, np, fid, context);
if (error)
goto out;
for (len = 0, cp = wbuf + SMB_SYMMAGICLEN;
cp < wbuf + SMB_SYMMAGICLEN + SMB_SYMLENLEN-1; cp++) {
if (*cp < '0' || *cp > '9') {
SMBWARNING("symlink length nonnumeric: %c (0x%x)\n", *cp, *cp);
return (EINVAL);
}
len *= 10;
len += *cp - '0';
}
if (len != np->n_size) {
SMBWARNING("symlink length payload changed from %u to %u\n",
(unsigned)np->n_size, len);
np->n_size = len;
}
target = (char *)(wbuf + SMB_SYMHDRLEN);
SMBSYMDEBUG_LOCK(np, "%s --> %s\n", np->n_name, target);
smbfs_update_symlink_cache(np, target, len);
error = uiomove(target, (int)len, uiop);
out:
SMB_FREE(wbuf, M_TEMP);
if (error) {
SMBWARNING_LOCK(np, "%s failed %d\n", np->n_name, error);
}
return (error);
}
int
smbfs_smb_unix_read_symlink(struct smb_share *share, struct smbnode *np,
struct uio *uiop, vfs_context_t context)
{
struct smb_t2rq *t2p;
int error;
struct mbchain *mbp;
struct mdchain *mdp;
char *ntwrkname = NULL;
char *target;
size_t targetlen;
size_t nmlen;
if (SSTOVC(share)->vc_flags & SMBV_SMB2) {
SMBERROR("smbfs_smb_unix_read_symlink not supported by SMB 2/3\n");
error = ENOTSUP;
return error;
}
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_QUERY_PATH_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, SMB_QFILEINFO_UNIX_LINK);
mb_put_uint32le(mbp, 0);
error = smbfs_fullpath(mbp, np, NULL, NULL, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error)
goto out;
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = SSTOVC(share)->vc_txmax;
error = smb_t2_request(t2p);
if (error)
goto out;
mdp = &t2p->t2_rdata;
if (mdp->md_top == NULL) {
error = EIO;
goto out;
}
m_fixhdr(mdp->md_top);
nmlen = mbuf_pkthdr_len(mdp->md_top);
SMBSYMDEBUG("network len of the symbolic link = %ld\n", nmlen);
SMB_MALLOC(ntwrkname, char *, nmlen, M_TEMP, M_WAITOK);
if (ntwrkname == NULL) {
error = ENOMEM;
} else {
error = md_get_mem(mdp, (void *)ntwrkname, nmlen, MB_MSYSTEM);
}
if (error)
goto out;
#ifdef DEBUG_SYMBOLIC_LINKS
smb_hexdump(__FUNCTION__, "Symlink: ", (u_char *)ntwrkname, nmlen);
#endif // DEBUG_SYMBOLIC_LINKS
nmlen -= 2;
targetlen = nmlen * 9 + 1;
SMB_MALLOC(target, char *, targetlen, M_TEMP, M_WAITOK | M_ZERO);
error = smb_convert_network_to_path(ntwrkname, nmlen, target,
&targetlen, '/', UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)));
if (!error) {
SMBSYMDEBUG_LOCK(np, "%s --> %s\n", np->n_name, target);
smbfs_update_symlink_cache(np, target, targetlen);
error = uiomove(target, (int)targetlen, uiop);
}
SMB_FREE(target, M_TEMP);
out:
SMB_FREE(ntwrkname, M_TEMP);
smb_t2_done(t2p);
if (error) {
SMBWARNING_LOCK(np, "%s failed %d\n", np->n_name, error);
}
return error;
}
int
smb1fs_smb_qstreaminfo(struct smb_share *share, struct smbnode *np,
const char *namep, size_t name_len,
const char *strmname,
uio_t uio, size_t *sizep,
uint64_t *strmsize, uint64_t *strmsize_alloc,
uint32_t *stream_flagsp,
vfs_context_t context)
{
struct smb_t2rq *t2p;
int error;
struct mbchain *mbp;
struct mdchain *mdp;
uint32_t next, nlen, used;
uint64_t stream_size, stream_alloc_size;
enum stream_types stype = kNoStream;
struct timespec ts;
int foundStream = (strmname) ? FALSE : TRUE;
char *streamName = NULL;
size_t streamNameLen = 0;
size_t in_name_len = name_len;
const char *fname;
uint32_t is_data_stream = 0;
int n_name_locked = 0;
if (namep == NULL) {
if ((np->n_fstatus & kNO_SUBSTREAMS) ||
(np->n_dosattr & SMB_EFA_REPARSE_POINT)) {
foundStream = FALSE;
error = ENOATTR;
goto done;
}
}
if (sizep)
*sizep = 0;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_QUERY_PATH_INFORMATION, 1, context, &t2p);
if (error)
return (error);
mbp = &t2p->t2_tparam;
mb_init(mbp);
if (VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU)
mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFORMATION);
else
mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
mb_put_uint32le(mbp, 0);
error = smbfs_fullpath(mbp, np, namep, &in_name_len, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error)
goto out;
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = SSTOVC(share)->vc_txmax;
error = smb_t2_request(t2p);
if (error)
goto out;
if (!SMB_UNICODE_STRINGS(SSTOVC(share))) {
error = ENOTSUP;
goto out;
}
mdp = &t2p->t2_rdata;
if (mdp->md_cur == NULL)
goto out;
do {
char *ntwrk_name = NULL;
stream_size = 0;
stream_alloc_size = 0;
if ((error = md_get_uint32le(mdp, &next)))
goto out;
used = 4;
if ((error = md_get_uint32le(mdp, &nlen)))
goto out;
used += 4;
if ((error = md_get_uint64le(mdp, &stream_size)))
goto out;
used += 8;
if ((error = md_get_uint64le(mdp, &stream_alloc_size)))
goto out;
used += 8;
if (nlen > SSTOVC(share)->vc_txmax) {
error = EBADRPC;
goto out;
}
SMB_MALLOC(ntwrk_name, char *, nlen, M_SMBFSDATA, M_WAITOK | M_ZERO);
if ((error = md_get_mem(mdp, ntwrk_name, nlen, MB_MSYSTEM))) {
SMB_FREE(ntwrk_name, M_SMBFSDATA);
goto out;
}
used += nlen;
if ((nlen > 1) && !ntwrk_name[nlen - 1] && !ntwrk_name[nlen - 2])
nlen -= 2;
streamNameLen = nlen;
streamName = smbfs_ntwrkname_tolocal(ntwrk_name, &streamNameLen,
SMB_UNICODE_STRINGS(SSTOVC(share)));
SMB_FREE(ntwrk_name, M_SMBFSDATA);
if (namep == NULL) {
lck_rw_lock_shared(&np->n_name_rwlock);
n_name_locked = 1;
fname = np->n_name;
}
else {
fname = namep;
}
if (smbfs_smb_undollardata(fname, streamName, &streamNameLen,
&is_data_stream)) {
const char *s;
if (n_name_locked) {
lck_rw_unlock_shared(&np->n_name_rwlock);
n_name_locked = 0;
}
s = streamName + 1;
if ((streamNameLen >= sizeof(SFM_RESOURCEFORK_NAME)) &&
(!strncasecmp(s, SFM_RESOURCEFORK_NAME, sizeof(SFM_RESOURCEFORK_NAME)))) {
stype |= kResourceFrk;
if (namep == NULL) {
lck_mtx_lock(&np->rfrkMetaLock);
np->rfrk_size = stream_size;
np->rfrk_alloc_size = stream_alloc_size;
nanouptime(&ts);
np->rfrk_cache_timer = ts.tv_sec;
lck_mtx_unlock(&np->rfrkMetaLock);
}
if ((uio == NULL) && strmname && (sizep == NULL)) {
s = SFM_RESOURCEFORK_NAME;
streamNameLen = sizeof(SFM_RESOURCEFORK_NAME);
} else {
s = XATTR_RESOURCEFORK_NAME;
streamNameLen = sizeof(XATTR_RESOURCEFORK_NAME);
}
if (uio && (stream_size == 0))
goto skipentry;
} else if ((streamNameLen >= sizeof(SFM_FINDERINFO_NAME)) &&
(!strncasecmp(s, SFM_FINDERINFO_NAME, sizeof(SFM_FINDERINFO_NAME)))) {
if (stream_size == 0)
goto skipentry;
stype |= kFinderInfo;
if ((uio == NULL) && strmname && (sizep == NULL)) {
s = SFM_FINDERINFO_NAME;
streamNameLen = sizeof(SFM_FINDERINFO_NAME);
}
else {
s = XATTR_FINDERINFO_NAME;
streamNameLen = sizeof(XATTR_FINDERINFO_NAME);
}
}
if (( (streamNameLen >= sizeof(SFM_DESKTOP_NAME)) &&
(!strncasecmp(s, SFM_DESKTOP_NAME, sizeof(SFM_DESKTOP_NAME)))) ||
((streamNameLen >= sizeof(SFM_IDINDEX_NAME)) &&
(!strncasecmp(s, SFM_IDINDEX_NAME, sizeof(SFM_IDINDEX_NAME))))) {
if (strmname && (!strncasecmp(SFM_DESKTOP_NAME, strmname, sizeof(SFM_DESKTOP_NAME)))) {
foundStream = TRUE;
}
goto skipentry;
} else if (uio)
uiomove(s, (int)streamNameLen, uio);
else if (!foundStream && strmname && strmsize) {
nlen = (uint32_t)strnlen(strmname, share->ss_maxfilenamelen+1);
if ((streamNameLen >= nlen) && (!strncasecmp(s, strmname, nlen))) {
*strmsize = stream_size;
*strmsize_alloc = stream_alloc_size;
foundStream = TRUE;
}
}
if (sizep)
*sizep += streamNameLen;
}
if (n_name_locked) {
lck_rw_unlock_shared(&np->n_name_rwlock);
n_name_locked = 0;
}
skipentry:
SMB_FREE(streamName, M_SMBFSDATA);
if (next > used) {
next -= used;
if (next > SSTOVC(share)->vc_txmax) {
error = EBADRPC;
goto out;
}
md_get_mem(mdp, NULL, next, MB_MSYSTEM);
}
} while (next && !error);
out:
if (streamName != NULL) {
SMB_FREE(streamName, M_SMBFSDATA);
}
smb_t2_done(t2p);
done:
if ((stype & kFinderInfo) != kFinderInfo) {
*stream_flagsp |= SMB_NO_FINDER_INFO;
if (namep == NULL) {
bzero(np->finfo, sizeof(np->finfo));
nanouptime(&ts);
np->finfo_cache_timer = ts.tv_sec;
}
}
if ((stype & kResourceFrk) != kResourceFrk) {
*stream_flagsp |= SMB_NO_RESOURCE_FORK;
if (namep == NULL) {
lck_mtx_lock(&np->rfrkMetaLock);
nanouptime(&ts);
np->rfrk_size = 0;
np->rfrk_alloc_size = 0;
np->rfrk_cache_timer = ts.tv_sec;
lck_mtx_unlock(&np->rfrkMetaLock);
}
}
if ((foundStream == FALSE) || (error == ENOENT))
error = ENOATTR;
return (error);
}
void
smbfs_unix_whoami(struct smb_share *share, struct smbmount *smp, vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
struct mdchain *mdp;
int error;
uint32_t ii;
uint32_t reserved;
size_t total_bytes;
uint32_t ntwrk_sids_cnt;
uint32_t ntwrk_sid_size;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_QUERY_FS_INFORMATION, 1, context, &t2p);
if (error)
return;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, SMB_QFS_POSIX_WHOAMI);
t2p->t2_maxpcount = 4;
t2p->t2_maxdcount = SSTOVC(share)->vc_txmax;
error = smb_t2_request(t2p);
if (error)
goto done;
mdp = &t2p->t2_rdata;
if (mdp->md_cur == NULL) {
error = EBADRPC;
goto done;
}
md_get_uint32le(mdp, NULL);
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, &smp->ntwrk_uid);
md_get_uint64le(mdp, &smp->ntwrk_gid);
md_get_uint32le(mdp, &smp->ntwrk_cnt_gid);
md_get_uint32le(mdp, &ntwrk_sids_cnt);
md_get_uint32le(mdp, &ntwrk_sid_size);
error = md_get_uint32le(mdp, &reserved);
if (error)
goto done;
SMBWARNING("network uid = %lld network gid = %lld supplementary group cnt = %d SID cnt = %d\n",
smp->ntwrk_uid, smp->ntwrk_gid, smp->ntwrk_cnt_gid, ntwrk_sids_cnt);
if (error || (reserved != 0) || ((int32_t)smp->ntwrk_cnt_gid < 0) || ((int32_t)ntwrk_sids_cnt < 0)) {
if (! error)
error = EBADRPC;
goto done;
}
if (smp->ntwrk_cnt_gid == 0)
goto sid_groups;
total_bytes = smp->ntwrk_cnt_gid * sizeof(uint64_t);
if ((total_bytes / sizeof(uint64_t)) != smp->ntwrk_cnt_gid) {
error = EBADRPC;
goto done;
}
if (total_bytes > SSTOVC(share)->vc_txmax) {
error = EBADRPC;
goto done;
}
SMB_MALLOC(smp->ntwrk_gids, uint64_t *, total_bytes, M_TEMP, M_WAITOK);
if (smp->ntwrk_gids == NULL) {
error = ENOMEM;
goto done;
}
for (ii = 0; ii < smp->ntwrk_cnt_gid; ii++) {
error = md_get_uint64le(mdp, &smp->ntwrk_gids[ii]);
if (error)
goto done;
SMBDEBUG("smp->ntwrk_gids[%d] = %lld\n", ii, smp->ntwrk_gids[ii]);
}
UNIX_CAPS(share) |= UNIX_QFS_POSIX_WHOAMI_CAP;
sid_groups:
if (share->ss_attributes & FILE_PERSISTENT_ACLS) {
smb_get_sid_list(share, smp, mdp,ntwrk_sids_cnt, ntwrk_sid_size);
}
done:
smb_t2_done(t2p);
if (error == EBADRPC)
SMBERROR("Parsing error reading the message\n");
if (error && smp->ntwrk_gids) {
SMB_FREE(smp->ntwrk_gids, M_TEMP);
smp->ntwrk_gids = NULL;
smp->ntwrk_cnt_gid = 0;
}
}
void
smbfs_unix_qfsattr(struct smb_share *share, vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
struct mdchain *mdp;
uint16_t majorv;
uint16_t minorv;
uint64_t cap;
int error;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_QUERY_FS_INFORMATION, 1, context, &t2p);
if (error)
return;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, SMB_QFS_UNIX_INFO);
t2p->t2_maxpcount = 4;
t2p->t2_maxdcount = 12;
error = smb_t2_request(t2p);
if (error)
goto done;
mdp = &t2p->t2_rdata;
if (mdp->md_cur == NULL) {
SMBWARNING("Parsing error reading the message\n");
goto done;
}
md_get_uint16le(mdp, &majorv);
md_get_uint16le(mdp, &minorv);
md_get_uint64le(mdp, &cap);
SMBWARNING("version %x.%x cap = %llx\n", majorv, minorv, cap);
UNIX_CAPS(share) = UNIX_QFS_UNIX_INFO_CAP | (cap & CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP);
if (UNIX_CAPS(share) & CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP) {
UNIX_CAPS(share) |= UNIX_QFILEINFO_UNIX_LINK_CAP | UNIX_SFILEINFO_UNIX_LINK_CAP |
UNIX_QFILEINFO_UNIX_INFO2_CAP | UNIX_SFILEINFO_UNIX_INFO2_CAP;
}
done:
smb_t2_done(t2p);
}
void
smb1fs_qfsattr(struct smb_share *share, vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
struct mdchain *mdp;
uint32_t nlen = 0;
int error;
size_t fs_nmlen;
char *fsname = NULL;
share->ss_fstype = SMB_FS_FAT;
share->ss_attributes = 0;
share->ss_maxfilenamelen = 255;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_QUERY_FS_INFORMATION, 1, context, &t2p);
if (error) {
return;
}
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, SMB_QFS_ATTRIBUTE_INFO);
t2p->t2_maxpcount = 4;
t2p->t2_maxdcount = 4 * 3 + 512;
error = smb_t2_request(t2p);
if (error) {
goto done;
}
mdp = &t2p->t2_rdata;
if (mdp->md_cur == NULL) {
error = EBADRPC;
}
else {
md_get_uint32le(mdp, &share->ss_attributes);
md_get_uint32le(mdp, &share->ss_maxfilenamelen);
if (share->ss_maxfilenamelen > (SMB_MAXFNAMELEN * 2)) {
SMBERROR("Illegal file name len %u\n", share->ss_maxfilenamelen);
share->ss_maxfilenamelen = 255;
}
error = md_get_uint32le(mdp, &nlen);
}
if (error) {
SMBWARNING("Server returned a bad SMB_QFS_ATTRIBUTE_INFO message\n");
SSTOVC(share)->vc_sopt.sv_caps &= ~SMB_CAP_UNIX;
goto done;
}
if ((nlen > 0) && (nlen < PATH_MAX)) {
char *ntwrkName;
SMB_MALLOC(ntwrkName, char *, nlen, M_SMBFSDATA, M_WAITOK | M_ZERO);
md_get_mem(mdp, ntwrkName, nlen, MB_MSYSTEM);
fs_nmlen = nlen;
fsname = smbfs_ntwrkname_tolocal(ntwrkName, &fs_nmlen,
SMB_UNICODE_STRINGS(SSTOVC(share)));
SMB_FREE(ntwrkName, M_SMBFSDATA);
if (fsname == NULL) {
goto done;
}
fs_nmlen += 1;
if (strncmp(fsname, "FAT", fs_nmlen) == 0)
share->ss_fstype = SMB_FS_FAT;
else if (strncmp(fsname, "FAT12", fs_nmlen) == 0)
share->ss_fstype = SMB_FS_FAT;
else if (strncmp(fsname, "FAT16", fs_nmlen) == 0)
share->ss_fstype = SMB_FS_FAT;
else if (strncmp(fsname, "FAT32", fs_nmlen) == 0)
share->ss_fstype = SMB_FS_FAT;
else if (strncmp(fsname, "CDFS", fs_nmlen) == 0)
share->ss_fstype = SMB_FS_CDFS;
else if (strncmp(fsname, "UDF", fs_nmlen) == 0)
share->ss_fstype = SMB_FS_UDF;
else if (strncmp(fsname, "NTFS", fs_nmlen) == 0)
share->ss_fstype = SMB_FS_NTFS_UNKNOWN;
SMBWARNING("%s/%s type '%s', attr 0x%x, maxfilename %d\n",
SSTOVC(share)->vc_srvname, share->ss_name, fsname,
share->ss_attributes, share->ss_maxfilenamelen);
if ((SSTOVC(share)->vc_flags & SMBV_NT4) && (share->ss_fstype & SMB_FS_NTFS_UNKNOWN))
share->ss_attributes |= FILE_NAMED_STREAMS;
if ((share->ss_fstype == SMB_FS_NTFS_UNKNOWN) && (share->ss_attributes & FILE_NAMED_STREAMS))
share->ss_fstype = SMB_FS_NTFS;
else if ((share->ss_fstype == SMB_FS_NTFS_UNKNOWN) && (UNIX_SERVER(SSTOVC(share))))
share->ss_fstype = SMB_FS_NTFS_UNIX;
}
done:
SMB_FREE(fsname, M_SMBSTR);
smb_t2_done(t2p);
}
int
smb1fs_statfs(struct smb_share *share, struct vfsstatfs *sbp, vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
struct mdchain *mdp;
uint32_t bpu, bsize32;
uint64_t s, t, f;
int error;
size_t xmax;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_QUERY_FS_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, SMB_QFS_SIZE_INFO);
t2p->t2_maxpcount = 4;
t2p->t2_maxdcount = (8 * 2) + (4 * 2);
error = smb_t2_request(t2p);
if (error) {
smb_t2_done(t2p);
return error;
}
mdp = &t2p->t2_rdata;
if (mdp->md_cur == NULL) {
SMBWARNING("Parsing error reading the message\n");
smb_t2_done(t2p);
return EBADRPC;
}
md_get_uint64le(mdp, &t);
md_get_uint64le(mdp, &f);
md_get_uint32le(mdp, &bpu);
md_get_uint32le(mdp, &bsize32);
s = bsize32;
s *= bpu;
while (s > 16 * 1024) {
if (t > LONG_MAX)
break;
s /= 2;
t *= 2;
f *= 2;
}
while (t > LONG_MAX) {
t /= 2;
f /= 2;
s *= 2;
}
sbp->f_bsize = (uint32_t)s;
sbp->f_blocks= t;
sbp->f_bfree = f;
sbp->f_bavail= f;
sbp->f_files = (-1);
sbp->f_ffree = (-1);
smb_t2_done(t2p);
xmax = max(SSTOVC(share)->vc_rxmax, SSTOVC(share)->vc_wxmax);
xmax = (PAGE_SIZE / (PAGE_SIZE - (xmax % PAGE_SIZE))) * xmax;
if (xmax > SMB_IOMAX)
sbp->f_iosize = SMB_IOMAX;
else
sbp->f_iosize = (SMB_IOMAX/xmax) * xmax;
return 0;
}
int
smbfs_smb_t2rename(struct smb_share *share, struct smbnode *np,
const char *tname, size_t tnmlen, int overwrite,
SMBFID *infid, vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
int32_t *ucslenp;
size_t outLen = 0;
int error, cerror;
SMBFID fid = 0;
int need_close = 0;
uint16_t smb1_fid = 0;
if (SSTOVC(share)->vc_flags & SMBV_SMB2) {
SMBERROR("smbfs_smb_t2rename not supported by SMB 2/3\n");
error = ENOTSUP;
return error;
}
if (infid) {
fid = *infid;
smb1_fid = (uint16_t) fid;
}
else {
fid = 0;
}
if (!(VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU))
return (ENOTSUP);
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_FILE_INFORMATION, 1, context, &t2p);
if (error)
return error;
if (!infid) {
error = smbfs_tmpopen(share, np, SMB2_DELETE, &fid, context);
if (error)
goto exit;
need_close = 1;
smb1_fid = fid;
}
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
mb_put_uint16le(mbp, SMB_SFILEINFO_RENAME_INFORMATION);
mb_put_uint16le(mbp, 0);
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_uint32le(mbp, overwrite);
mb_put_uint32le(mbp, 0);
ucslenp = (int32_t *)mb_reserve(mbp, sizeof(int32_t));
error = smb_put_dmem(mbp, tname, tnmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), &outLen);
if (!error)
error = mb_put_uint16le(mbp, 0);
if (error)
goto exit;
*ucslenp = htolel((int32_t)outLen);
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = 0;
error = smb_t2_request(t2p);
exit:;
if (need_close == 1) {
cerror = smbfs_tmpclose(share, np, fid, context);
if (cerror) {
SMBWARNING("error %d closing fid %llx\n", cerror, fid);
}
}
smb_t2_done(t2p);
return (error);
}
int
smbfs_delete_openfile(struct smb_share *share, struct smbnode *dnp,
struct smbnode *np, vfs_context_t context)
{
struct proc *p;
SMBFID fid = 0;
int error, cerror;
char s_name[32];
size_t s_namlen;
int i, j, k;
int need_close = 0;
int need_hide = 0;
int samba_bug = 0;
struct smbmount *smp = np->n_mount;
uint64_t ino = 0;
uint64_t hashval = 0;
char *new_name = NULL;
char *old_name = NULL;
if (context == NULL) {
return ENOTSUP;
}
p = vfs_context_proc(context);
if (!(SSTOVC(share)->vc_flags & SMBV_SMB2)) {
if (!(VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU)) {
return EBUSY;
}
error = smbfs_tmpopen(share, np, SMB2_DELETE, &fid, context);
if (error) {
return (error);
}
need_close = 1;
need_hide = 1;
}
if (SSTOVC(share)->vc_misc_flags & SMBV_HAS_FILEIDS) {
lck_rw_lock_shared(&np->n_name_rwlock);
ino = smb2fs_smb_file_id_get(smp, np->n_ino, np->n_name);
lck_rw_unlock_shared(&np->n_name_rwlock);
MAKE_DELETED_NAME((char*) s_name, sizeof(s_name), ino);
s_namlen = strlen((char *) s_name);
error = smbfs_smb_rename(share, np, dnp, s_name, s_namlen, context);
}
else {
s_namlen = snprintf(s_name, sizeof(s_name), ".smbdeleteAAA%04x4.4",
proc_pid(p));
if (s_namlen >= sizeof(s_name)) {
error = ENOENT;
goto out;
}
i = j = k = 0;
do {
if ((SSTOVC(share)->vc_flags & SMBV_SMB2) ||
(samba_bug == 1)) {
error = smbfs_smb_rename(share, np, dnp, s_name, s_namlen,
context);
}
else {
error = smbfs_smb_t2rename(share, np, s_name, s_namlen, 0, &fid,
context);
if (((error == ENOENT) || (error == ENOTSUP)) &&
SMBTOV(dnp) &&
(samba_bug == 0)) {
if (need_close == 1) {
(void)smbfs_tmpclose(share, np, fid, context);
need_close = 0;
}
samba_bug = 1;
continue;
}
}
if (error && (error != EEXIST)) {
if (smbfs_smb_query_info(share, dnp, VREG,
s_name, s_namlen, NULL, context) == 0) {
error = EEXIST;
}
else {
break;
}
}
if (error) {
if (s_name[10]++ >= 'z')
s_name[10] = 'A';
if (++i > ('z' - 'A' + 1)) {
i = 0;
if (s_name[11]++ >= 'z')
s_name[11] = 'A';
if (++j > ('z' - 'A' + 1)) {
j = 0;
if (s_name[12]++ >= 'z')
s_name[12] = 'A';
if (++k > ('z' - 'A' + 1)) {
error = EINVAL;
}
}
}
}
} while (error == EEXIST);
}
if (error) {
goto out;
}
if (need_hide) {
(void)smbfs_set_hidden_bit(share, dnp, VREG,
s_name, s_namlen,
TRUE, context);
}
if ((SSTOVC(share)->vc_flags & SMBV_SMB2) || (samba_bug == 1)) {
np->n_flag |= NDELETEONCLOSE;
}
else {
cerror = smbfs_smb_markfordelete(share, fid, context);
if (cerror) {
np->n_flag |= NDELETEONCLOSE;
}
}
out:
if (need_close == 1) {
cerror = smbfs_tmpclose(share, np, fid, context);
if (cerror) {
SMBWARNING("error %d closing fid %llx\n", cerror, fid);
}
}
if (!error) {
lck_rw_lock_exclusive(&np->n_name_rwlock);
new_name = smb_strndup(s_name, s_namlen);
old_name = np->n_name;
if (!(SSTOVC(share)->vc_misc_flags & SMBV_HAS_FILEIDS) ||
!(np->n_flag & NDELETEONCLOSE)) {
smb_vhashrem(np);
}
if (new_name) {
np->n_name = new_name;
np->n_nmlen = s_namlen;
np->n_flag |= NMARKEDFORDLETE;
if (!(SSTOVC(share)->vc_misc_flags & SMBV_HAS_FILEIDS) &&
!(np->n_flag & NDELETEONCLOSE)) {
hashval = smbfs_hash(NULL, 0, np->n_name, np->n_nmlen);
smb_vhashadd(np, hashval);
}
SMB_FREE(old_name, M_SMBNODENAME);
}
lck_rw_unlock_exclusive(&np->n_name_rwlock);
}
else {
error = EBUSY;
}
return(error);
}
int
smb1fs_smb_flush(struct smb_share *share, SMBFID fid, vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
int error;
uint16_t smb1_fid = (uint16_t) fid;
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_FLUSH, 0, context);
if (error) {
goto done;
}
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if ((error == EBADF) && (rqp->sr_flags & SMBR_REXMIT))
error = 0;
smb_rq_done(rqp);
done:
if (error) {
SMBWARNING("smbfs_smb_flush failed error = %d\n", error);
}
return (error);
}
int
smb1fs_seteof(struct smb_share *share, SMBFID fid, uint64_t newsize,
vfs_context_t context)
{
struct mbchain *mbp;
int error;
uint16_t smb1_fid = (uint16_t) fid;
struct smb_t2rq *t2p;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_FILE_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
if (VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU)
mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFORMATION);
else
mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFO);
mb_put_uint16le(mbp, 0);
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_uint64le(mbp, newsize);
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = 0;
error = smb_t2_request(t2p);
smb_t2_done(t2p);
return error;
}
int
smb1fs_set_allocation(struct smb_share *share, SMBFID fid, uint64_t newsize,
vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
int error;
uint16_t smb1_fid = (uint16_t) fid;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_FILE_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
if (VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU)
mb_put_uint16le(mbp, SMB_SFILEINFO_ALLOCATION_INFORMATION);
else
mb_put_uint16le(mbp, SMB_SFILEINFO_ALLOCATION_INFO);
mb_put_uint16le(mbp, 0);
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_uint64le(mbp, newsize);
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = 0;
error = smb_t2_request(t2p);
smb_t2_done(t2p);
return error;
}
int
smbfs_seteof(struct smb_share *share, struct smbnode *np, SMBFID fid,
uint64_t newsize, vfs_context_t context)
{
int error;
error = smbfs_smb_seteof(share, fid, newsize, context);
if (error && (error != EBADF)) {
SMBWARNING("smbfs_node_seteof failed error = %d\n", error);
} else if (!error) {
np->n_flag &= ~NNEEDS_EOF_SET;
np->n_flag |= NNEEDS_FLUSH;
}
return error;
}
int
smbfs_smb_fsync(struct smb_share *share, struct smbnode *np, vfs_context_t context)
{
int error;
SMBFID fid = 0;
SMB_LOG_KTRACE(SMB_DBG_SMB_FSYNC | DBG_FUNC_START, np->n_flag, 0, 0, 0, 0);
if ((np->n_flag & (NNEEDS_EOF_SET | NNEEDS_FLUSH)) == 0) {
error = 0;
goto done;
}
if ((np->f_refcnt <= 0) || (!SMBTOV(np)) || (!vnode_isreg(SMBTOV(np)))) {
error = 0;
goto done;
}
error = smbfs_smb_reopen_file(share, np, context);
if (error) {
SMBDEBUG_LOCK(np, " %s waiting to be revoked\n", np->n_name);
goto done;
}
SMB_LOG_KTRACE(SMB_DBG_SMB_FSYNC | DBG_FUNC_NONE, 0xabc001, error, 0, 0, 0);
if (FindFileRef(SMBTOV(np), vfs_context_proc(context), kAccessWrite,
kCheckDenyOrLocks, 0, 0, NULL, &fid)) {
fid = np->f_fid;
if ((fid == 0) ||
((np->f_accessMode & kAccessWrite) != kAccessWrite)) {
error = 0;
SMB_LOG_KTRACE(SMB_DBG_SMB_FSYNC | DBG_FUNC_NONE,
0xabc002, error, 0, 0, 0);
goto done;
}
}
if (np->n_flag & NNEEDS_EOF_SET) {
error = smbfs_seteof(share, np, fid, np->n_size, context);
SMB_LOG_KTRACE(SMB_DBG_SMB_FSYNC | DBG_FUNC_NONE,
0xabc003, error, 0, 0, 0);
if (error) {
goto done;
}
}
error = smbfs_smb_flush(share, fid, context);
if (!error)
np->n_flag &= ~NNEEDS_FLUSH;
SMB_LOG_KTRACE(SMB_DBG_SMB_FSYNC | DBG_FUNC_NONE, 0xabc004, error, 0, 0, 0);
done:
SMB_LOG_KTRACE(SMB_DBG_SMB_FSYNC | DBG_FUNC_END, error, 0, 0, 0, 0);
return error;
}
int
smb1fs_smb_query_info(struct smb_share *share, struct smbnode *np,
const char *name, size_t len, uint32_t *in_attr,
vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
struct mdchain *mdp;
uint8_t wc = 0;
int error;
uint16_t attr;
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_QUERY_INFORMATION, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
error = smbfs_fullpath(mbp, np, name, &len, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (!error) {
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
}
if (!error && in_attr) {
smb_rq_getreply(rqp, &mdp);
md_get_uint8(mdp, &wc);
error = md_get_uint16le(mdp, &attr);
*in_attr = attr;
}
smb_rq_done(rqp);
return error;
}
int
smb1fs_smb_setpattr(struct smb_share *share, struct smbnode *np,
const char *name, size_t len,
uint16_t attr, vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
uint32_t time;
int error;
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_SET_INFORMATION, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, attr);
time = 0;
mb_put_uint32le(mbp, time);
mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
do {
error = smbfs_fullpath(mbp, np, name, &len, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error)
break;
if (SMB_UNICODE_STRINGS(SSTOVC(share))) {
mb_put_padbyte(mbp);
mb_put_uint8(mbp, 0);
}
mb_put_uint8(mbp, 0);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if (error)
break;
} while(0);
smb_rq_done(rqp);
return error;
}
int
smbfs_set_hidden_bit(struct smb_share *share, struct smbnode *dnp, enum vtype vnode_type,
const char *name, size_t len,
Boolean hideit, vfs_context_t context)
{
int error;
uint32_t attr;
error = smbfs_smb_query_info(share, dnp, vnode_type,
name, len,
&attr, context);
if (error) {
return error;
}
if (hideit && !(attr & SMB_EFA_HIDDEN)) {
attr |= SMB_EFA_HIDDEN;
} else if (!hideit && (attr & SMB_EFA_HIDDEN)) {
attr &= ~SMB_EFA_HIDDEN;
} else {
return 0;
}
return smbfs_smb_setpattr(share, dnp, vnode_type,
name, len,
attr, context);
}
int
smbfs_set_unix_info2(struct smb_share *share, struct smbnode *np,
struct timespec *crtime, struct timespec *mtime,
struct timespec *atime, uint64_t fsize, uint64_t perms,
uint32_t FileFlags, uint32_t FileFlagsMask, vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
uint64_t tm;
uint32_t ftype;
int error;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_PATH_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, SMB_SFILEINFO_UNIX_INFO2);
mb_put_uint32le(mbp, 0);
error = smbfs_fullpath(mbp, np, NULL, NULL, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error) {
smb_t2_done(t2p);
return error;
}
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_uint64le(mbp, fsize);
tm = SMB_SIZE_NO_CHANGE;
mb_put_uint64le(mbp, tm);
mb_put_uint64le(mbp, 0);
if (atime)
smb_time_local2NT(atime, &tm, FALSE);
else
tm = 0;
mb_put_uint64le(mbp, tm);
if (mtime)
smb_time_local2NT(mtime, &tm, FALSE);
else
tm = 0;
mb_put_uint64le(mbp, tm);
tm = SMB_UID_NO_CHANGE;
mb_put_uint64le(mbp, tm);
tm = SMB_GID_NO_CHANGE;
mb_put_uint64le(mbp, tm);
ftype = SMB_DEFAULT_NO_CHANGE;
mb_put_uint32le(mbp, ftype);
tm = SMB_DEFAULT_NO_CHANGE;
mb_put_uint64le(mbp, tm);
tm = SMB_DEFAULT_NO_CHANGE;
mb_put_uint64le(mbp, tm);
tm = SMB_DEFAULT_NO_CHANGE;
mb_put_uint64le(mbp, tm);
mb_put_uint64le(mbp, perms);
tm = SMB_DEFAULT_NO_CHANGE;
mb_put_uint64le(mbp, tm);
if (crtime)
smb_time_local2NT(crtime, &tm, FALSE);
else
tm = 0;
mb_put_uint64le(mbp, tm);
mb_put_uint32le(mbp, FileFlags);
mb_put_uint32le(mbp, FileFlagsMask);
t2p->t2_maxpcount = 24;
t2p->t2_maxdcount = 116;
error = smb_t2_request(t2p);
smb_t2_done(t2p);
return error;
}
int
smb1fs_smb_setpattrNT(struct smb_share *share, struct smbnode *np,
uint32_t attr, struct timespec *crtime,
struct timespec *mtime, struct timespec *atime,
vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
uint64_t tm;
int error;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_PATH_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
if (VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU)
mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION);
else
mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFO);
mb_put_uint32le(mbp, 0);
error = smbfs_fullpath(mbp, np, NULL, NULL, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error) {
smb_t2_done(t2p);
return error;
}
mbp = &t2p->t2_tdata;
mb_init(mbp);
tm = 0;
if (crtime) {
smb_time_local2NT(crtime, &tm, (share->ss_fstype == SMB_FS_FAT));
}
mb_put_uint64le(mbp, tm);
tm = 0;
if (atime) {
smb_time_local2NT(atime, &tm, (share->ss_fstype == SMB_FS_FAT));
}
mb_put_uint64le(mbp, tm);
tm = 0;
if (mtime) {
smb_time_local2NT(mtime, &tm, (share->ss_fstype == SMB_FS_FAT));
}
mb_put_uint64le(mbp, tm);
tm = 0;
mb_put_uint64le(mbp, tm);
mb_put_uint32le(mbp, attr);
mb_put_uint32le(mbp, 0);
t2p->t2_maxpcount = 24;
t2p->t2_maxdcount = 56;
error = smb_t2_request(t2p);
smb_t2_done(t2p);
return error;
}
int
smb1fs_smb_setfattrNT(struct smb_share *share, uint32_t attr, SMBFID fid,
struct timespec *crtime, struct timespec *mtime,
struct timespec *atime, vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
uint64_t tm;
int error;
uint16_t smb1_fid = (uint16_t) fid;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_FILE_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
if (VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU)
mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION);
else
mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFO);
mb_put_uint16le(mbp, 0);
mbp = &t2p->t2_tdata;
mb_init(mbp);
tm = 0;
if (crtime) {
smb_time_local2NT(crtime, &tm, (share->ss_fstype == SMB_FS_FAT));
}
mb_put_uint64le(mbp, tm);
tm = 0;
if (atime) {
smb_time_local2NT(atime, &tm, (share->ss_fstype == SMB_FS_FAT));
}
mb_put_uint64le(mbp, tm);
tm = 0;
if (mtime) {
smb_time_local2NT(mtime, &tm, (share->ss_fstype == SMB_FS_FAT));
}
mb_put_uint64le(mbp, tm);
tm = 0;
mb_put_uint64le(mbp, tm);
mb_put_uint32le(mbp, attr);
mb_put_uint32le(mbp, 0);
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = 0;
error = smb_t2_request(t2p);
smb_t2_done(t2p);
return error;
}
int
smb1fs_smb_ntcreatex(struct smb_share *share, struct smbnode *np,
uint32_t rights, uint32_t shareMode, enum vtype vt,
SMBFID *fidp, const char *name, size_t in_nmlen,
uint32_t disp, int xattr, struct smbfattr *fap,
int do_create, vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
int unix_info2 = ((UNIX_CAPS(share) & UNIX_QFILEINFO_UNIX_INFO2_CAP)) ? TRUE : FALSE;
struct mbchain *mbp;
struct mdchain *mdp;
uint8_t wc;
uint32_t lint, createopt, efa;
uint64_t llint;
int error;
uint16_t fid, *namelenp;
size_t nmlen = in_nmlen;
int need_close = 0;
SMBFID temp_fid = 0;
char *snamep = NULL;
DBG_ASSERT(fap);
bzero(fap, sizeof(*fap));
nanouptime(&fap->fa_reqtime);
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_NT_CREATE_ANDX, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint8(mbp, 0);
namelenp = (uint16_t *)mb_reserve(mbp, sizeof(uint16_t));
mb_put_uint32le(mbp, NTCREATEX_FLAGS_EXTENDED);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, rights);
mb_put_uint64le(mbp, 0);
efa = (vt == VDIR) ? SMB_EFA_DIRECTORY : SMB_EFA_NORMAL;
if (disp != FILE_OPEN && !xattr) {
if (efa == SMB_EFA_NORMAL)
efa |= SMB_EFA_ARCHIVE;
if (name && (*name == '.'))
efa |= SMB_EFA_HIDDEN;
}
mb_put_uint32le(mbp, efa);
mb_put_uint32le(mbp, shareMode);
mb_put_uint32le(mbp, disp);
createopt = 0;
if (disp != FILE_OPEN) {
if (vt == VDIR)
createopt |= NTCREATEX_OPTIONS_DIRECTORY;
}
if (share->ss_attributes & FILE_SUPPORTS_REPARSE_POINTS) {
createopt |= NTCREATEX_OPTIONS_OPEN_REPARSE_POINT;
if (np && (np->n_dosattr & SMB_EFA_OFFLINE)) {
createopt &= ~NTCREATEX_OPTIONS_OPEN_REPARSE_POINT;
}
}
mb_put_uint32le(mbp, createopt);
mb_put_uint32le(mbp, NTCREATEX_IMPERSONATION_IMPERSONATION);
mb_put_uint8(mbp, 0);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
do {
uint16_t resourceType = 0;
uint8_t sep = xattr ? ':' : '\\';
if ((np->n_vnode) && (vnode_isnamedstream(np->n_vnode)) && (!name) && (!xattr)) {
lck_rw_lock_shared(&np->n_name_rwlock);
snamep = smb_strndup(np->n_sname, np->n_snmlen);
name = snamep;
lck_rw_unlock_shared(&np->n_name_rwlock);
nmlen = np->n_snmlen;
sep = ':';
}
if (name == NULL)
nmlen = 0;
error = smbfs_fullpath(mbp, np, name, &nmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), sep);
if (error)
break;
*namelenp = htoles(nmlen);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if (error)
break;
smb_rq_getreply(rqp, &mdp);
error = md_get_uint8(mdp, &wc);
if (!error) {
md_get_uint8(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint8(mdp, NULL);
error = md_get_uint16(mdp, &fid);
need_close = 1;
}
if (error) {
error = EBADRPC;
break;
}
if ( (wc != NTCREATEX_NORMAL_WDCNT) && (wc != NTCREATEX_EXTENDED_WDCNT) &&
(wc != NTCREATEX_BRKEN_SPEC_26_WDCNT) ) {
if (need_close == 1) {
temp_fid = fid;
smbfs_smb_close(share, temp_fid, context);
}
error = EBADRPC;
break;
}
md_get_uint32le(mdp, &fap->fa_created_disp);
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &fap->fa_crtime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &fap->fa_atime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &fap->fa_mtime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &fap->fa_chtime);
}
md_get_uint32le(mdp, &lint);
fap->fa_attr = lint;
if (fap->fa_attr & SMB_EFA_DIRECTORY) {
fap->fa_valid_mask |= FA_VTYPE_VALID;
}
fap->fa_vtype = (fap->fa_attr & SMB_EFA_DIRECTORY) ? VDIR : VREG;
md_get_uint64le(mdp, &llint);
fap->fa_data_alloc = llint;
md_get_uint64le(mdp, &llint);
fap->fa_size = llint;
md_get_uint16le(mdp, &resourceType);
if ((resourceType == kFileTypeDisk) &&
((!UNIX_SERVER(SSTOVC(share))) || (SSTOVC(share)->vc_flags & SMBV_DARWIN))) {
md_get_uint16le(mdp, &fap->fa_fstatus);
fap->fa_valid_mask |= FA_FSTATUS_VALID;
} else {
md_get_uint16le(mdp, NULL);
}
md_get_uint8(mdp, NULL);
if (name)
break;
if (wc == NTCREATEX_EXTENDED_WDCNT) {
int maxAccessRightsError;
uint8_t VolumeGID[16];
uint64_t fileID = 0;
uint32_t guestMaxAccessRights = 0;
md_get_mem(mdp, (caddr_t)VolumeGID, sizeof(VolumeGID), MB_MSYSTEM);
md_get_uint64le(mdp, &fileID);
maxAccessRightsError = md_get_uint32le(mdp, &np->maxAccessRights);
if (!maxAccessRightsError)
maxAccessRightsError = md_get_uint32le(mdp, &guestMaxAccessRights);
if (maxAccessRightsError) {
np->n_flag |= NO_EXTENDEDOPEN;
SMB_LOG_AUTH_LOCK(np, "Error %d getting extended reply for %s\n", maxAccessRightsError, np->n_name);
} else {
SMB_LOG_AUTH_LOCK(np, "%s fileID = %llx maxAccessRights = 0x%x guestMaxAccessRights = 0x%x\n",
np->n_name, fileID, np->maxAccessRights, guestMaxAccessRights);
np->n_flag &= ~NO_EXTENDEDOPEN;
lck_rw_lock_shared(&np->n_parent_rwlock);
if (((np->maxAccessRights & SMB2_DELETE) != SMB2_DELETE) &&
(!np->n_parent || (np->n_parent->maxAccessRights & SMB2_FILE_DELETE_CHILD))) {
np->maxAccessRights |= SMB2_DELETE;
}
lck_rw_unlock_shared(&np->n_parent_rwlock);
}
} else {
np->n_flag |= NO_EXTENDEDOPEN;
}
if (np->n_flag & NO_EXTENDEDOPEN) {
np->maxAccessRights = SA_RIGHT_FILE_ALL_ACCESS | STD_RIGHT_ALL_ACCESS;
SMBDEBUG_LOCK(np, "Extended reply not supported: %s setting maxAccessRights to 0x%x rights = 0x%x\n",
np->n_name, np->maxAccessRights, rights);
}
np->maxAccessRightChTime = fap->fa_chtime;
} while(0);
smb_rq_done(rqp);
if (error) {
if (snamep) {
SMB_FREE(snamep, M_SMBSTR);
}
return error;
}
if (fidp) {
*fidp = fid;
}
if (vt != VDIR) {
lck_mtx_lock(&np->f_openStateLock);
if (np->f_openState & kInReopen) {
lck_mtx_unlock(&np->f_openStateLock);
goto WeAreDone;
}
lck_mtx_unlock(&np->f_openStateLock);
}
if (xattr) {
goto WeAreDone;
}
if (do_create == TRUE) {
DBG_ASSERT(name != NULL);
fap->fa_ino = smbfs_getino(np, name, in_nmlen);
goto WeAreDone;
}
if (np->n_vnode == NULL) {
goto WeAreDone;
}
if (unix_info2) {
fap->fa_unix = TRUE;
fap->fa_flags_mask = EXT_REQUIRED_BY_MAC;
fap->fa_nlinks = np->n_nlinks;
fap->fa_attr &= ~SMB_EFA_RDONLY;
fap->fa_attr |= (np->n_dosattr & SMB_EFA_RDONLY);
fap->fa_valid_mask |= FA_VTYPE_VALID;
fap->fa_vtype = vnode_vtype(np->n_vnode);
if (vnode_isdir(np->n_vnode))
fap->fa_attr |= SMB_EFA_DIRECTORY;
else
fap->fa_attr &= ~SMB_EFA_DIRECTORY;
fap->fa_chtime = np->n_chtime;
}
smbfs_attr_cacheenter(share, np->n_vnode, fap, TRUE, context);
WeAreDone:
if (snamep) {
SMB_FREE(snamep, M_SMBSTR);
}
return (0);
}
int
smbfs_tmpopen(struct smb_share *share, struct smbnode *np, uint32_t rights,
SMBFID *fidp, vfs_context_t context)
{
int searchOpenFiles;
int error = 0;
struct smbfattr fattr;
if (!np->n_vnode || vnode_isdir(np->n_vnode))
searchOpenFiles = FALSE;
else {
error = smbfs_smb_reopen_file(share, np, context);
if (error) {
SMBDEBUG_LOCK(np, " %s waiting to be revoked\n", np->n_name);
return(error);
}
if (rights & (SMB2_DELETE | SMB2_WRITE_DAC | SMB2_WRITE_OWNER))
searchOpenFiles = FALSE;
else if (rights & SMB2_FILE_WRITE_ATTRIBUTES)
searchOpenFiles = FALSE;
else
searchOpenFiles = TRUE;
}
if (searchOpenFiles && SMBTOV(np)) {
uint16_t accessMode = 0;
if (rights & (SMB2_READ_CONTROL | SMB2_FILE_READ_DATA))
accessMode |= kAccessRead;
if (rights & (SMB2_FILE_APPEND_DATA | SMB2_FILE_WRITE_DATA))
accessMode |= kAccessWrite;
if (rights & SMB2_FILE_WRITE_ATTRIBUTES)
accessMode |= kAccessWrite;
if ( (np->f_fid != 0)
&& ((accessMode & np->f_accessMode) == accessMode) ) {
np->f_refcnt++;
*fidp = np->f_fid;
return (0);
}
if (np->f_refcnt && context &&
(FindFileRef(SMBTOV(np), vfs_context_proc(context), accessMode,
kAnyMatch, 0, 0, NULL, fidp) == 0)) {
np->f_refcnt++;
return (0);
}
}
uint32_t shareMode = NTCREATEX_SHARE_ACCESS_ALL;
error = smbfs_smb_ntcreatex(share, np,
rights, shareMode, (np->n_vnode && vnode_isdir(np->n_vnode)) ? VDIR : VREG,
fidp, NULL, 0,
FILE_OPEN, FALSE, &fattr,
FALSE, NULL, context);
if (error) {
SMBWARNING_LOCK(np, "%s failed to open: error = %d\n", np->n_name, error);
}
return (error);
}
int
smbfs_tmpclose(struct smb_share *share, struct smbnode *np, SMBFID fid,
vfs_context_t context)
{
struct fileRefEntry *entry = NULL;
vnode_t vp = SMBTOV(np);
if (!vp || vnode_isdir(vp) ||
((fid != np->f_fid) &&
(FindFileEntryByFID(vp, fid, &entry)))) {
return(smbfs_smb_close(share, fid, context));
}
if (np->f_refcnt == 1) {
return(smbfs_close(share, vp, 0, context));
}
if (np->f_refcnt > 0) {
np->f_refcnt--;
}
return (0);
}
int
smb1fs_smb_openread(struct smb_share *share, struct smbnode *np, SMBFID *fid,
uint32_t rights, uio_t uio, size_t *sizep, const char *name,
struct timespec *mtime, vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
struct mdchain *mdp;
uint8_t wc, cmd;
int error = 0;
uint16_t *namelenp, *nextWdCntOffset, nextOffset;
uint64_t eof;
uint16_t residhi, residlo, off, doff;
uint32_t resid;
uint32_t len = (uint32_t)uio_resid(uio);
size_t nmlen = strnlen(name, share->ss_maxfilenamelen+1);
uint16_t smb1_fid;
if ((SSTOVC(share)->vc_txmax - SMB_MAX_CHAIN_READ) < len)
return(ENOTSUP);
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_NT_CREATE_ANDX, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, SMB_COM_READ_ANDX);
mb_put_uint8(mbp, 0);
nextWdCntOffset = (uint16_t *)mb_reserve(mbp, sizeof(uint16_t));
mb_put_uint8(mbp, 0);
namelenp = (uint16_t *)mb_reserve(mbp, sizeof(uint16_t));
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, rights);
mb_put_uint64le(mbp, 0);
mb_put_uint32le(mbp, SMB_EFA_NORMAL);
if (rights & SMB2_FILE_WRITE_DATA) {
mb_put_uint32le(mbp, (NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_DELETE));
mb_put_uint32le(mbp, FILE_OPEN_IF);
} else {
mb_put_uint32le(mbp, NTCREATEX_SHARE_ACCESS_ALL);
mb_put_uint32le(mbp, FILE_OPEN);
}
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, NTCREATEX_IMPERSONATION_IMPERSONATION);
mb_put_uint8(mbp, 0);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
error = smbfs_fullpath(mbp, np, name, &nmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), ':');
if (error)
goto exit;
*namelenp = htoles(nmlen);
smb_rq_bend(rqp);
mb_put_padbyte(mbp);
*nextWdCntOffset = htoles(mb_fixhdr(mbp));
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, (uint32_t)uio_offset(uio));
mb_put_uint16le(mbp, (uint16_t)len);
mb_put_uint16le(mbp, (uint16_t)len);
mb_put_uint32le(mbp, len >> 16);
mb_put_uint16le(mbp, (uint16_t)len);
mb_put_uint32le(mbp, (uint32_t)(uio_offset(uio) >> 32));
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if (error)
goto exit;
smb_rq_getreply(rqp, &mdp);
if (md_get_uint8(mdp, &wc) != 0 || (wc != 34 && wc != 42)) {
error = EINVAL;
goto exit;
}
md_get_uint8(mdp, &cmd);
md_get_uint8(mdp, NULL);
md_get_uint16le(mdp, &nextOffset);
md_get_uint8(mdp, NULL);
md_get_uint16(mdp, &smb1_fid);
*fid = smb1_fid;
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
if (mtime) {
uint64_t llint;
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, mtime);
}
else
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, &eof);
if (sizep)
*sizep = (size_t)eof;
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_uint16(mdp, NULL);
if (cmd != SMB_COM_READ_ANDX) {
if ((rights & SMB2_FILE_WRITE_DATA) && fid)
error = 0;
else
error = ENOENT;
goto exit;
}
off = nextOffset;
m_fixhdr(mdp->md_top);
if (nextOffset > mbuf_pkthdr_len(mdp->md_top)) {
error = EINVAL;
goto exit;
}
nextOffset -= (SMB_HDRLEN + SMB_CREATEXRLEN + SMB_BCOUNT_LEN);
if (nextOffset != 0)
md_get_mem(mdp, NULL, nextOffset, MB_MSYSTEM);
if (md_get_uint8(mdp, &wc) != 0 || (wc != 12)) {
error = EINVAL;
goto exit;
}
off++;
md_get_uint8(mdp, NULL);
off++;
md_get_uint8(mdp, NULL);
off++;
md_get_uint16le(mdp, NULL);
off += 2;
md_get_uint16le(mdp, NULL);
off += 2;
md_get_uint16le(mdp, NULL);
off += 2;
md_get_uint16le(mdp, NULL);
off += 2;
md_get_uint16le(mdp, &residlo);
off += 2;
md_get_uint16le(mdp, &doff);
off += 2;
md_get_uint16le(mdp, &residhi);
off += 2;
resid = (residhi << 16) | residlo;
md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
off += 4*2;
md_get_uint16le(mdp, NULL);
off += 2;
if (doff > off)
md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
if (resid)
error = md_get_uio(mdp, uio, resid);
exit:
smb_rq_done(rqp);
return (error);
}
int
smb1fs_smb_open_maxaccess(struct smb_share *share, struct smbnode *dnp,
const char *namep, size_t name_len,
SMBFID *fidp, uint32_t *max_accessp,
vfs_context_t context)
{
#pragma unused(name_len)
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
struct mdchain *mdp;
uint8_t wc;
int error = 0;
uint16_t *namelenp, nextOffset;
size_t nmlen = strnlen(namep, share->ss_maxfilenamelen+1);
uint16_t smb1_fid;
*fidp = 0;
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_NT_CREATE_ANDX, 0, context);
if (error) {
return error;
}
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint8(mbp, 0);
namelenp = (uint16_t *)mb_reserve(mbp, sizeof(uint16_t));
mb_put_uint32le(mbp, NTCREATEX_FLAGS_EXTENDED);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, SMB2_FILE_READ_DATA);
mb_put_uint64le(mbp, 0);
mb_put_uint32le(mbp, SMB_EFA_NORMAL);
mb_put_uint32le(mbp, NTCREATEX_SHARE_ACCESS_ALL);
mb_put_uint32le(mbp, FILE_OPEN);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, NTCREATEX_IMPERSONATION_IMPERSONATION);
mb_put_uint8(mbp, 0);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
error = smbfs_fullpath_stream(mbp, dnp,
namep, NULL,
&nmlen, 0, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error)
goto exit;
*namelenp = htoles(nmlen);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if (error)
goto exit;
smb_rq_getreply(rqp, &mdp);
if (md_get_uint8(mdp, &wc) != 0 ||
(wc != NTCREATEX_NORMAL_WDCNT && wc != NTCREATEX_EXTENDED_WDCNT)) {
error = EINVAL;
goto exit;
}
md_get_uint8(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_uint16le(mdp, &nextOffset);
md_get_uint8(mdp, NULL);
md_get_uint16(mdp, &smb1_fid);
*fidp = smb1_fid;
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint8(mdp, NULL);
if (wc == NTCREATEX_EXTENDED_WDCNT) {
int maxAccessRightsError;
uint8_t VolumeGID[16];
uint64_t fileID = 0;
uint32_t guestMaxAccessRights = 0;
uint32_t max_access_rights = 0;
md_get_mem(mdp, (caddr_t)VolumeGID, sizeof(VolumeGID), MB_MSYSTEM);
md_get_uint64le(mdp, &fileID);
maxAccessRightsError = md_get_uint32le(mdp, &max_access_rights);
if (!maxAccessRightsError) {
maxAccessRightsError = md_get_uint32le(mdp, &guestMaxAccessRights);
}
if (maxAccessRightsError) {
*max_accessp = SA_RIGHT_FILE_ALL_ACCESS | STD_RIGHT_ALL_ACCESS;
}
else {
if (((max_access_rights & SMB2_DELETE) != SMB2_DELETE) &&
(!dnp || (dnp->maxAccessRights & SMB2_FILE_DELETE_CHILD))) {
max_access_rights |= SMB2_DELETE;
}
*max_accessp = max_access_rights;
}
}
else {
SMBDEBUG("Server does not support max access, returning full access\n");
*max_accessp = SA_RIGHT_FILE_ALL_ACCESS | STD_RIGHT_ALL_ACCESS;
}
exit:
smb_rq_done(rqp);
return (error);
}
int
smb1fs_smb_open_read(struct smb_share *share, struct smbnode *dnp,
const char *namep, size_t name_len,
const char *strm_namep, size_t strm_name_len,
SMBFID *fidp, uio_t uio, size_t *sizep,
uint32_t *max_accessp,
vfs_context_t context)
{
#pragma unused(name_len)
#pragma unused(strm_name_len)
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
struct mdchain *mdp;
uint8_t wc, cmd;
int error = 0;
int error2 = 0;
uint16_t *namelenp, *nextWdCntOffset, nextOffset;
uint64_t eof;
uint16_t residhi, residlo, off, doff;
uint32_t resid;
uint32_t len = (uint32_t)uio_resid(uio);
size_t nmlen = strnlen(namep, share->ss_maxfilenamelen+1);
size_t snmlen = strnlen(strm_namep, share->ss_maxfilenamelen+1);
uint16_t smb1_fid;
uint32_t rights = SMB2_FILE_READ_DATA;
if ((SSTOVC(share)->vc_txmax - SMB_MAX_CHAIN_READ) < len)
return(ENOTSUP);
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_NT_CREATE_ANDX, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, SMB_COM_READ_ANDX);
mb_put_uint8(mbp, 0);
nextWdCntOffset = (uint16_t *)mb_reserve(mbp, sizeof(uint16_t));
mb_put_uint8(mbp, 0);
namelenp = (uint16_t *)mb_reserve(mbp, sizeof(uint16_t));
if (max_accessp) {
mb_put_uint32le(mbp, NTCREATEX_FLAGS_EXTENDED);
}
else {
mb_put_uint32le(mbp, 0);
}
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, rights);
mb_put_uint64le(mbp, 0);
mb_put_uint32le(mbp, SMB_EFA_NORMAL);
if (rights & SMB2_FILE_WRITE_DATA) {
mb_put_uint32le(mbp, (NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_DELETE));
mb_put_uint32le(mbp, FILE_OPEN_IF);
} else {
mb_put_uint32le(mbp, NTCREATEX_SHARE_ACCESS_ALL);
mb_put_uint32le(mbp, FILE_OPEN);
}
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, NTCREATEX_IMPERSONATION_IMPERSONATION);
mb_put_uint8(mbp, 0);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
error = smbfs_fullpath_stream(mbp, dnp,
namep, strm_namep,
&nmlen, snmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error)
goto exit;
*namelenp = htoles(nmlen);
smb_rq_bend(rqp);
mb_put_padbyte(mbp);
*nextWdCntOffset = htoles(mb_fixhdr(mbp));
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, (uint32_t)uio_offset(uio));
mb_put_uint16le(mbp, (uint16_t)len);
mb_put_uint16le(mbp, (uint16_t)len);
mb_put_uint32le(mbp, len >> 16);
mb_put_uint16le(mbp, (uint16_t)len);
mb_put_uint32le(mbp, (uint32_t)(uio_offset(uio) >> 32));
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if (error) {
}
smb_rq_getreply(rqp, &mdp);
if (md_get_uint8(mdp, &wc) != 0 ||
(wc != NTCREATEX_NORMAL_WDCNT && wc != NTCREATEX_EXTENDED_WDCNT)) {
if (error == 0) {
error = EINVAL;
}
goto exit;
}
error2 = md_get_uint8(mdp, &cmd);
if (error2) {
if (error == 0) {
error = error2;
}
goto exit;
}
error2 = md_get_uint8(mdp, NULL);
if (error2) {
if (error == 0) {
error = error2;
}
goto exit;
}
error2 = md_get_uint16le(mdp, &nextOffset);
if (error2) {
if (error == 0) {
error = error2;
}
goto exit;
}
error2 = md_get_uint8(mdp, NULL);
if (error2) {
if (error == 0) {
error = error2;
}
goto exit;
}
error2 = md_get_uint16(mdp, &smb1_fid);
if (error2) {
if (error == 0) {
error = error2;
}
goto exit;
}
*fidp = smb1_fid;
if (error) {
goto exit;
}
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint32le(mdp, NULL);
md_get_uint64le(mdp, NULL);
md_get_uint64le(mdp, &eof);
if (sizep)
*sizep = (size_t)eof;
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint8(mdp, NULL);
if (wc == NTCREATEX_EXTENDED_WDCNT) {
int maxAccessRightsError;
uint8_t VolumeGID[16];
uint64_t fileID = 0;
uint32_t guestMaxAccessRights = 0;
uint32_t max_access_rights = 0;
md_get_mem(mdp, (caddr_t)VolumeGID, sizeof(VolumeGID), MB_MSYSTEM);
md_get_uint64le(mdp, &fileID);
maxAccessRightsError = md_get_uint32le(mdp, &max_access_rights);
if (!maxAccessRightsError) {
maxAccessRightsError = md_get_uint32le(mdp, &guestMaxAccessRights);
}
if (maxAccessRightsError) {
if (max_accessp) {
*max_accessp = SA_RIGHT_FILE_ALL_ACCESS | STD_RIGHT_ALL_ACCESS;
}
}
else {
if (((max_access_rights & SMB2_DELETE) != SMB2_DELETE) &&
(!dnp || (dnp->maxAccessRights & SMB2_FILE_DELETE_CHILD))) {
max_access_rights |= SMB2_DELETE;
}
if (max_accessp) {
*max_accessp = max_access_rights;
}
}
}
else {
if (max_accessp) {
SMBDEBUG("Server does not support max access, returning full access\n");
*max_accessp = SA_RIGHT_FILE_ALL_ACCESS | STD_RIGHT_ALL_ACCESS;
}
}
if (cmd != SMB_COM_READ_ANDX) {
if ((rights & SMB2_FILE_WRITE_DATA) && fidp)
error = 0;
else
error = ENOENT;
goto exit;
}
off = nextOffset;
m_fixhdr(mdp->md_top);
if (nextOffset > mbuf_pkthdr_len(mdp->md_top)) {
error = EINVAL;
goto exit;
}
if (wc == NTCREATEX_EXTENDED_WDCNT) {
nextOffset -= SMB_HDRLEN;
nextOffset -= 69;
nextOffset -= 32;
}
else {
md_get_uint16(mdp, NULL);
nextOffset -= (SMB_HDRLEN + SMB_CREATEXRLEN + SMB_BCOUNT_LEN);
}
if (nextOffset != 0) {
md_get_mem(mdp, NULL, nextOffset, MB_MSYSTEM);
}
if (md_get_uint8(mdp, &wc) != 0 || (wc != 12)) {
error = EINVAL;
goto exit;
}
off++;
md_get_uint8(mdp, NULL);
off++;
md_get_uint8(mdp, NULL);
off++;
md_get_uint16le(mdp, NULL);
off += 2;
md_get_uint16le(mdp, NULL);
off += 2;
md_get_uint16le(mdp, NULL);
off += 2;
md_get_uint16le(mdp, NULL);
off += 2;
md_get_uint16le(mdp, &residlo);
off += 2;
md_get_uint16le(mdp, &doff);
off += 2;
md_get_uint16le(mdp, &residhi);
off += 2;
resid = (residhi << 16) | residlo;
md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
off += 4*2;
md_get_uint16le(mdp, NULL);
off += 2;
if (doff > off)
md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
if (resid)
error = md_get_uio(mdp, uio, resid);
exit:
smb_rq_done(rqp);
return (error);
}
int
smbfs_smb_open_file(struct smb_share *share, struct smbnode *np,
uint32_t rights, uint32_t shareMode, SMBFID *fidp,
const char *name, size_t nmlen, int xattr,
struct smbfattr *fap, vfs_context_t context)
{
int error;
int do_create;
uint32_t disp;
if ((np->n_flag & N_ISRSRCFRK) && !xattr) {
disp = FILE_OPEN_IF;
do_create = TRUE;
} else {
disp = FILE_OPEN;
do_create = FALSE;
}
error = smbfs_smb_ntcreatex(share, np,
rights, shareMode, VREG,
fidp, name, nmlen,
disp, xattr, fap,
do_create, NULL, context);
return (error);
}
int
smbfs_smb_open_xattr(struct smb_share *share, struct smbnode *np, uint32_t rights,
uint32_t shareMode, SMBFID *fidp, const char *name,
size_t *sizep, vfs_context_t context)
{
size_t nmlen = strnlen(name, share->ss_maxfilenamelen+1);
int error;
struct smbfattr fattr;
error = smbfs_smb_open_file(share, np, rights, shareMode, fidp,
name, nmlen, TRUE, &fattr, context);
if (!error && sizep)
*sizep = (size_t)fattr.fa_size;
return(error);
}
int
smbfs_smb_reopen_file(struct smb_share *share, struct smbnode *np,
vfs_context_t context)
{
int error = 0;
struct timespec n_mtime = np->n_mtime;
u_quad_t n_size = np->n_size;
struct smbfattr fattr;
if (context != SSTOVC(share)->vc_iod->iod_context) {
while (share->ss_flags & SMBS_RECONNECTING) {
SMBDEBUG("SMBS_RECONNECTING Going to sleep! \n");
msleep(&share->ss_flags, 0, PWAIT, "smbfs_smb_reopen_file", NULL);
}
}
lck_mtx_lock(&np->f_openStateLock);
if (np->f_openState & kNeedRevoke) {
lck_mtx_unlock(&np->f_openStateLock);
return EIO;
} else if (!(np->f_openState & kNeedReopen) &&
!(np->f_openState & kInReopen)) {
lck_mtx_unlock(&np->f_openStateLock);
return 0;
}
np->f_openState &= ~kNeedReopen;
np->f_openState |= kInReopen;
lck_mtx_unlock(&np->f_openStateLock);
DBG_ASSERT(np->f_refcnt);
error = smbfs_smb_open_file(share, np, np->f_rights,
NTCREATEX_SHARE_ACCESS_ALL,
&np->f_fid, NULL, 0, FALSE, &fattr,
context);
if (error) {
SMBERROR_LOCK(np, "Reopen %s failed because the open call failed!\n", np->n_name);
}
if (error || (np->f_smbflock == NULL)) {
goto exit;
}
if ((!(timespeccmp(&n_mtime, &fattr.fa_mtime, ==))) ||
(n_size != fattr.fa_size)) {
if (n_size != np->n_size) {
SMBERROR_LOCK(np, "Reopen %s failed because the size has changed was 0x%lld now 0x%lld!\n",
np->n_name, n_size, fattr.fa_size);
}
else {
SMBERROR_LOCK(np, "Reopen %s failed because the modify time has changed was %lds %ldns now %lds %ldns!\n",
np->n_name, n_mtime.tv_sec, n_mtime.tv_nsec,
fattr.fa_mtime.tv_sec, fattr.fa_mtime.tv_nsec);
}
error = EIO;
}
else {
struct smbfs_flock *flk = np->f_smbflock;
error = smbfs_smb_lock(share, SMB_LOCK_EXCL, np->f_fid,
flk->lck_pid, flk->start, flk->len,
0, context);
if (error) {
SMBERROR_LOCK(np, "Reopen %s failed because we could not reestablish the lock! \n", np->n_name);
}
}
if (error) {
(void)smbfs_smb_close(share, np->f_fid, context);
}
exit:
lck_mtx_lock(&np->f_openStateLock);
np->f_openState &= ~kInReopen;
if (context && (error || (np->f_openState & kNeedReopen))) {
char errbuf[32];
int pid = proc_pid(vfs_context_proc(context));
proc_name(pid, &errbuf[0], 32);
SMBERROR_LOCK(np, "Warning: pid %d(%.*s) reopening of %s failed with error %d\n",
pid, 32, &errbuf[0], np->n_name, error);
np->f_openState |= kNeedRevoke;
error = EIO;
}
lck_mtx_unlock(&np->f_openStateLock);
return(error);
}
int
smb1fs_smb_close(struct smb_share *share, SMBFID fid, vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
uint32_t time;
int error;
uint16_t smb1_fid = (uint16_t) fid;
DBG_ASSERT(smb1_fid);
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_CLOSE, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
time = -1;
mb_put_uint32le(mbp, time);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
if ((error == ENOTCONN) || (error == ENXIO) || (error == EBADF))
error = 0;
return error;
}
int
smbfs_smb_create(struct smb_share *share, struct smbnode *dnp,
const char *in_name, size_t in_nmlen, uint32_t rights,
SMBFID *fidp, uint32_t disp, int xattr, struct smbfattr *fap,
vfs_context_t context)
{
const char *name = in_name;
size_t nmlen = in_nmlen;
SMBFID fid = 0;
int error;
uint32_t desired_access = rights;
uint32_t share_access = NTCREATEX_SHARE_ACCESS_ALL;
uint64_t create_flags = SMB2_CREATE_DO_CREATE | SMB2_CREATE_GET_MAX_ACCESS;
uint32_t ntstatus = 0;
char *file_namep = NULL, *stream_namep = NULL;
size_t file_name_len = 0, stream_name_len = 0;
if (SSTOVC(share)->vc_flags & SMBV_SMB2) {
if (!xattr) {
file_namep = (char *) in_name;
file_name_len = in_nmlen;
}
else {
create_flags |= SMB2_CREATE_IS_NAMED_STREAM;
stream_namep = (char *) in_name;
stream_name_len = in_nmlen;
}
error = smb2fs_smb_cmpd_create(share, dnp,
file_namep, file_name_len,
stream_namep, stream_name_len,
desired_access, VREG,
share_access, disp,
create_flags, &ntstatus,
fidp, fap,
NULL, context);
}
else {
error = smbfs_smb_ntcreatex(share, dnp,
desired_access, share_access, VREG,
&fid, name, nmlen,
disp, xattr, fap,
TRUE, NULL, context);
if (fidp) {
*fidp = fid;
}
else {
if (!error) {
(void)smbfs_smb_close(share, fid, context);
}
}
}
return error;
}
static int
smbfs_posix_unlink(struct smb_share *share, struct smbnode *np,
vfs_context_t context, const char *name, size_t nmlen)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
int error;
uint32_t isDir = (vnode_isdir(np->n_vnode)) ? 1 : 0;
error = smb_t2_alloc(SSTOCP(share), SMB_TRANS2_SET_PATH_INFORMATION, 1, context, &t2p);
if (error)
return error;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, SMB_SFILEINFO_POSIX_UNLINK);
mb_put_uint32le(mbp, 0);
error = smbfs_fullpath(mbp, np, name, &nmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error) {
smb_t2_done(t2p);
return error;
}
mbp = &t2p->t2_tdata;
mb_init(mbp);
mb_put_uint32le(mbp, isDir);
t2p->t2_maxpcount = 2;
t2p->t2_maxdcount = SSTOVC(share)->vc_txmax;
error = smb_t2_request(t2p);
smb_t2_done(t2p);
return error;
}
int
smb1fs_smb_delete(struct smb_share *share, struct smbnode *np, const char *name,
size_t nmlen, int xattr, vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
int error;
if (!xattr && (UNIX_CAPS(share) & UNIX_SFILEINFO_POSIX_UNLINK_CAP)) {
error = smbfs_posix_unlink(share, np, context, name, nmlen);
if (error == EPERM) {
int chmod_error;
uint64_t vamode = np->n_mode | S_IWUSR;
chmod_error = smbfs_set_unix_info2(share, np, NULL, NULL, NULL, SMB_SIZE_NO_CHANGE,
vamode, SMB_FLAGS_NO_CHANGE, SMB_FLAGS_NO_CHANGE, context);
if (chmod_error == 0) {
error = smbfs_posix_unlink(share, np, context, name, nmlen);
}
}
if (error != ENOTSUP) {
return error;
} else {
UNIX_CAPS(share) &= ~UNIX_SFILEINFO_POSIX_UNLINK_CAP;
}
}
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_DELETE, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, SMB_EFA_SYSTEM | SMB_EFA_HIDDEN);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
error = smbfs_fullpath(mbp, np, name, &nmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), xattr ? ':' : '\\');
if (!error) {
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
}
if ((error == ENOENT) && (rqp->sr_flags & SMBR_REXMIT))
error = 0;
smb_rq_done(rqp);
return error;
}
int
smb1fs_smb_rename(struct smb_share *share, struct smbnode *src,
struct smbnode *tdnp, const char *tname, size_t tnmlen,
vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
int error, retest = 0;
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_RENAME, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, (vnode_isdir(SMBTOV(src)) ? SMB_EFA_DIRECTORY : 0) |
SMB_EFA_SYSTEM | SMB_EFA_HIDDEN);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
do {
error = smbfs_fullpath(mbp, src, NULL, NULL, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error)
break;
mb_put_uint8(mbp, SMB_DT_ASCII);
error = smbfs_fullpath(mbp, tdnp, tname, &tnmlen, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (error)
break;
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
} while(0);
if ((error == ENOENT) && (rqp->sr_flags & SMBR_REXMIT))
retest = 1;
smb_rq_done(rqp);
if (error && retest) {
if ((smbfs_smb_query_info(share, src, VREG, NULL, 0, NULL, context) == ENOENT) &&
(smbfs_smb_query_info(share, tdnp, VREG, tname, tnmlen, NULL, context) == 0))
error = 0;
}
return error;
}
int
smbfs_smb_mkdir(struct smb_share *share, struct smbnode *dnp, const char *name,
size_t len, struct smbfattr *fap, vfs_context_t context)
{
SMBFID fid = 0;
int error = 0;
uint32_t desired_access = SMB2_FILE_READ_DATA;
uint32_t share_access = NTCREATEX_SHARE_ACCESS_ALL;
uint64_t create_flags = SMB2_CREATE_DO_CREATE | SMB2_CREATE_GET_MAX_ACCESS;
uint32_t ntstatus = 0;
if (SSTOVC(share)->vc_flags & SMBV_SMB2) {
error = smb2fs_smb_cmpd_create(share, dnp,
name, len,
NULL, 0,
desired_access, VDIR,
share_access, FILE_CREATE,
create_flags, &ntstatus,
NULL, fap,
NULL, context);
}
else {
error = smbfs_smb_ntcreatex(share, dnp,
desired_access, share_access, VDIR,
&fid, name, len,
FILE_CREATE, 0, fap,
TRUE, NULL, context);
if (!error) {
(void)smbfs_smb_close(share, fid, context);
}
}
return error;
}
int
smb1fs_smb_rmdir(struct smb_share *share, struct smbnode *np, vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
int error;
error = smb_rq_init(rqp, SSTOCP(share), SMB_COM_DELETE_DIRECTORY, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
error = smbfs_fullpath(mbp, np, NULL, NULL, UTF_SFM_CONVERSIONS,
SMB_UNICODE_STRINGS(SSTOVC(share)), '\\');
if (!error) {
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
}
if ((error == ENOENT) && (rqp->sr_flags & SMBR_REXMIT))
error = 0;
smb_rq_done(rqp);
return error;
}
static int
smbfs_smb_trans2find2(struct smbfs_fctx *ctx, vfs_context_t context)
{
struct smb_t2rq *t2p;
struct mbchain *mbp;
struct mdchain *mdp;
uint16_t tw, flags;
size_t len;
int error;
if (ctx->f_t2) {
smb_t2_done(ctx->f_t2);
ctx->f_t2 = NULL;
}
ctx->f_flags &= ~SMBFS_RDD_GOTRNAME;
flags = FIND2_RETURN_RESUME_KEYS | FIND2_CLOSE_ON_EOS;
if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
flags |= FIND2_CLOSE_AFTER_REQUEST;
ctx->f_flags |= SMBFS_RDD_NOCLOSE;
}
if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
error = smb_t2_alloc(SSTOCP(ctx->f_share), SMB_TRANS2_FIND_FIRST2, 1, context, &t2p);
if (error)
return error;
ctx->f_t2 = t2p;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_uint16le(mbp, ctx->f_attrmask);
mb_put_uint16le(mbp, ctx->f_searchCount);
mb_put_uint16le(mbp, flags);
mb_put_uint16le(mbp, ctx->f_infolevel);
mb_put_uint32le(mbp, 0);
len = ctx->f_lookupNameLen;
error = smbfs_fullpath(mbp, ctx->f_dnp, ctx->f_lookupName, &len,
ctx->f_sfm_conversion,
SMB_UNICODE_STRINGS(SSTOVC(ctx->f_share)), '\\');
if (error)
return error;
} else {
error = smb_t2_alloc(SSTOCP(ctx->f_share), SMB_TRANS2_FIND_NEXT2, 1, context, &t2p);
if (error)
return error;
ctx->f_t2 = t2p;
mbp = &t2p->t2_tparam;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
mb_put_uint16le(mbp, ctx->f_searchCount);
mb_put_uint16le(mbp, ctx->f_infolevel);
mb_put_uint32le(mbp, ctx->f_rkey);
mb_put_uint16le(mbp, flags);
if (ctx->f_rname) {
mb_put_mem(mbp, ctx->f_rname, ctx->f_rnamelen, MB_MSYSTEM);
}
if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_share)))
mb_put_uint8(mbp, 0);
mb_put_uint8(mbp, 0);
}
t2p->t2_maxpcount = 5 * 2;
t2p->t2_maxdcount = SSTOVC(ctx->f_share)->vc_txmax;
error = smb_t2_request(t2p);
if (error) {
if (error == ENOENT) {
ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
}
return error;
}
mdp = &t2p->t2_rparam;
if (mdp->md_cur == NULL) {
SMBWARNING("Parsing error reading the message\n");
return EBADRPC;
}
if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0)
return error;
ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
ctx->f_sfm_conversion = UTF_SFM_CONVERSIONS;
}
if ((error = md_get_uint16le(mdp, &tw)) != 0)
return error;
ctx->f_ecnt = tw;
if ((error = md_get_uint16le(mdp, &tw)) != 0)
return error;
if (tw || ctx->f_ecnt == 0)
ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
if ((error = md_get_uint16le(mdp, &tw)) != 0)
return error;
if ((error = md_get_uint16le(mdp, &tw)) != 0)
return error;
if (ctx->f_ecnt == 0)
return ENOENT;
ctx->f_rnameofs = tw;
mdp = &t2p->t2_rdata;
if (mdp->md_top == NULL) {
SMBERROR("bug: ecnt = %d, but data is NULL (please report)\n", ctx->f_ecnt);
ctx->f_ecnt = 0;
ctx->f_flags |= SMBFS_RDD_EOF;
return ENOENT;
}
m_fixhdr(mdp->md_top);
if (mbuf_pkthdr_len(mdp->md_top) == 0) {
SMBERROR("bug: ecnt = %d, but m_len = 0 and m_next = %p (please report)\n",
ctx->f_ecnt, mbuf_next(mbp->mb_top));
ctx->f_ecnt = 0;
ctx->f_flags |= SMBFS_RDD_EOF;
return ENOENT;
}
ctx->f_eofs = 0;
return 0;
}
static int
smbfs_ntwrk_findclose(struct smbfs_fctx *ctx, vfs_context_t context)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
int error;
error = smb_rq_init(rqp, SSTOCP(ctx->f_share), SMB_COM_FIND_CLOSE2, 0, context);
if (error)
return error;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
return error;
}
int
smb1fs_smb_findclose(struct smbfs_fctx *ctx, vfs_context_t context)
{
if (ctx->f_t2)
smb_t2_done(ctx->f_t2);
if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0)
smbfs_ntwrk_findclose(ctx, context);
smb_share_rele(ctx->f_share, context);
if (ctx->f_LocalName) {
SMB_FREE(ctx->f_LocalName, M_SMBFSDATA);
}
if (ctx->f_NetworkNameBuffer) {
SMB_FREE(ctx->f_NetworkNameBuffer, M_SMBFSDATA);
}
if (ctx->f_rname) {
SMB_FREE(ctx->f_rname, M_SMBFSDATA);
}
SMB_FREE(ctx, M_SMBFSDATA);
return 0;
}
int
smb1fs_smb_findnext(struct smbfs_fctx *ctx, vfs_context_t context)
{
struct mdchain *mdp;
struct smb_t2rq *t2p;
uint32_t next, dattr, resumekey = 0;
uint64_t llint;
int error, cnt;
uint32_t fxsz, recsz;
struct timespec ts;
uint32_t eaSize;
if (ctx->f_ecnt == 0) {
if (ctx->f_flags & SMBFS_RDD_EOF)
return ENOENT;
nanouptime(&ts);
error = smbfs_smb_trans2find2(ctx, context);
if (error)
return error;
ctx->f_attr.fa_reqtime = ts;
}
t2p = ctx->f_t2;
mdp = &t2p->t2_rdata;
ctx->f_NetworkNameLen = 0;
ctx->f_attr.fa_uid = KAUTH_UID_NONE;
ctx->f_attr.fa_gid = KAUTH_GID_NONE;
switch (ctx->f_infolevel) {
case SMB_FIND_BOTH_DIRECTORY_INFO:
md_get_uint32le(mdp, &next);
md_get_uint32le(mdp, &resumekey);
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &ctx->f_attr.fa_crtime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &ctx->f_attr.fa_atime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &ctx->f_attr.fa_mtime);
}
md_get_uint64le(mdp, &llint);
if (llint) {
smb_time_NT2local(llint, &ctx->f_attr.fa_chtime);
}
md_get_uint64le(mdp, &llint);
ctx->f_attr.fa_size = llint;
md_get_uint64le(mdp, &llint);
ctx->f_attr.fa_data_alloc = llint;
md_get_uint32le(mdp, &dattr);
ctx->f_attr.fa_attr = dattr;
if ((SSTOVC(ctx->f_share)->vc_flags & SMBV_DARWIN) ||
(ctx->f_attr.fa_attr & SMB_EFA_DIRECTORY)) {
ctx->f_attr.fa_valid_mask |= FA_VTYPE_VALID;
}
ctx->f_attr.fa_vtype = (ctx->f_attr.fa_attr & SMB_EFA_DIRECTORY) ? VDIR : VREG;
md_get_uint32le(mdp, &ctx->f_NetworkNameLen);
fxsz = 64;
ctx->f_attr.fa_valid_mask |= FA_REPARSE_TAG_VALID;
md_get_uint32le(mdp, &eaSize);
if (ctx->f_attr.fa_attr & SMB_EFA_REPARSE_POINT) {
ctx->f_attr.fa_reparse_tag = eaSize;
if (ctx->f_attr.fa_reparse_tag == IO_REPARSE_TAG_SYMLINK) {
ctx->f_attr.fa_valid_mask |= FA_VTYPE_VALID;
ctx->f_attr.fa_vtype = VLNK;
}
} else {
ctx->f_attr.fa_reparse_tag = IO_REPARSE_TAG_RESERVED_ZERO;
}
md_get_uint8(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_mem(mdp, NULL, 24, MB_MSYSTEM);
fxsz += 30;
recsz = next ? next : fxsz + ctx->f_NetworkNameLen;
break;
case SMB_FIND_FILE_UNIX_INFO2:
md_get_uint32le(mdp, &next);
md_get_uint32le(mdp, &resumekey);
md_get_uint64le(mdp, &llint);
ctx->f_attr.fa_size = llint;
md_get_uint64le(mdp, &llint);
ctx->f_attr.fa_data_alloc = llint;
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, &ctx->f_attr.fa_chtime);
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, &ctx->f_attr.fa_atime);
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, &ctx->f_attr.fa_mtime);
md_get_uint64le(mdp, &llint);
ctx->f_attr.fa_uid = llint;
md_get_uint64le(mdp, &llint);
ctx->f_attr.fa_gid = llint;
md_get_uint32le(mdp, &dattr);
if (dattr & EXT_UNIX_DIR) {
ctx->f_attr.fa_attr |= SMB_EFA_DIRECTORY;
ctx->f_attr.fa_vtype = VDIR;
} else {
ctx->f_attr.fa_attr &= ~SMB_EFA_DIRECTORY;
if (dattr & EXT_UNIX_SYMLINK)
ctx->f_attr.fa_vtype = VLNK;
else
ctx->f_attr.fa_vtype = VREG;
}
md_get_uint64le(mdp, &llint);
md_get_uint64le(mdp, &llint);
md_get_uint64le(mdp, &llint);
md_get_uint64le(mdp, &llint);
ctx->f_attr.fa_permissions = llint;
ctx->f_attr.fa_valid_mask |= FA_UNIX_MODES_VALID;
md_get_uint64le(mdp, &llint);
ctx->f_attr.fa_nlinks = llint;
md_get_uint64le(mdp, &llint);
if (llint)
smb_time_NT2local(llint, &ctx->f_attr.fa_crtime);
md_get_uint32le(mdp, &dattr);
md_get_uint32le(mdp, &ctx->f_attr.fa_flags_mask);
if (ctx->f_attr.fa_flags_mask & EXT_HIDDEN) {
if (dattr & EXT_HIDDEN)
ctx->f_attr.fa_attr |= SMB_EFA_HIDDEN;
else
ctx->f_attr.fa_attr &= ~SMB_EFA_HIDDEN;
}
if (ctx->f_attr.fa_flags_mask & EXT_IMMUTABLE) {
if (dattr & EXT_IMMUTABLE)
ctx->f_attr.fa_attr |= SMB_EFA_RDONLY;
else
ctx->f_attr.fa_attr &= ~SMB_EFA_RDONLY;
}
if (ctx->f_attr.fa_flags_mask & EXT_DO_NOT_BACKUP) {
if (dattr & EXT_DO_NOT_BACKUP)
ctx->f_attr.fa_attr &= ~SMB_EFA_ARCHIVE;
else
ctx->f_attr.fa_attr |= SMB_EFA_ARCHIVE;
}
ctx->f_attr.fa_unix = TRUE;
md_get_uint32le(mdp, &ctx->f_NetworkNameLen);
fxsz = 128;
recsz = next ? next : fxsz + ctx->f_NetworkNameLen;
break;
default:
SMBERROR("unexpected info level %d\n", ctx->f_infolevel);
return EINVAL;
}
if ((size_t)ctx->f_NetworkNameLen > ctx->f_MaxNetworkNameBufferSize)
ctx->f_NetworkNameLen = (uint32_t)ctx->f_MaxNetworkNameBufferSize;
error = md_get_mem(mdp, ctx->f_NetworkNameBuffer, ctx->f_NetworkNameLen, MB_MSYSTEM);
if (error)
return error;
if (next) {
cnt = next - ctx->f_NetworkNameLen - fxsz;
if (cnt > 0)
md_get_mem(mdp, NULL, cnt, MB_MSYSTEM);
else if (cnt < 0) {
SMBERROR("out of sync\n");
return EBADRPC;
}
}
if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_share))) {
if ((ctx->f_NetworkNameLen > 1) && (ctx->f_NetworkNameBuffer[ctx->f_NetworkNameLen - 1] == 0) &&
(ctx->f_NetworkNameBuffer[ctx->f_NetworkNameLen - 2] == 0))
ctx->f_NetworkNameLen -= 2;
} else {
if (ctx->f_NetworkNameLen && ctx->f_NetworkNameBuffer[ctx->f_NetworkNameLen - 1] == 0)
ctx->f_NetworkNameLen--;
}
if (ctx->f_NetworkNameLen == 0)
return EBADRPC;
ctx->f_rkey = resumekey;
next = ctx->f_eofs + recsz;
if (ctx->f_rnameofs && ((ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0) &&
((ctx->f_rnameofs >= ctx->f_eofs) && (ctx->f_rnameofs < next))) {
if (ctx->f_rnamelen < ctx->f_NetworkNameLen) {
if (ctx->f_rname) {
SMB_FREE(ctx->f_rname, M_SMBFSDATA);
}
SMB_MALLOC(ctx->f_rname, char *, ctx->f_NetworkNameLen, M_SMBFSDATA, M_WAITOK);
}
ctx->f_rnamelen = ctx->f_NetworkNameLen;
bcopy(ctx->f_NetworkNameBuffer, ctx->f_rname, ctx->f_NetworkNameLen);
ctx->f_flags |= SMBFS_RDD_GOTRNAME;
}
ctx->f_eofs = next;
ctx->f_ecnt--;
return 0;
}
int
smbfs_smb_findopen(struct smb_share *share, struct smbnode *dnp,
const char *lookupName, size_t lookupNameLen,
struct smbfs_fctx **ctxpp, int wildCardLookup,
vfs_context_t context)
{
struct smbfs_fctx *ctx;
int error = 0;
SMB_MALLOC(ctx, struct smbfs_fctx *, sizeof(*ctx), M_SMBFSDATA,
M_WAITOK | M_ZERO);
if (ctx == NULL) {
return ENOMEM;
}
ctx->f_share = share;
smb_share_ref(ctx->f_share);
ctx->f_dnp = dnp;
ctx->f_flags |= SMBFS_RDD_FINDFIRST;
if (!wildCardLookup) {
ctx->f_sfm_conversion = UTF_SFM_CONVERSIONS;
ctx->f_flags |= SMBFS_RDD_FINDSINGLE;
ctx->f_searchCount = 1;
}
if (UNIX_CAPS(share) & UNIX_FIND_FILE_UNIX_INFO2_CAP) {
if (wildCardLookup) {
ctx->f_searchCount = SSTOVC(share)->vc_txmax / SMB_FIND_FILE_UNIX_INFO2_MIN_LEN;
}
ctx->f_infolevel = SMB_FIND_FILE_UNIX_INFO2;
} else {
if (wildCardLookup) {
ctx->f_searchCount = SSTOVC(share)->vc_txmax / SMB_FIND_BOTH_DIRECTORY_INFO_MIN_LEN;
}
if (SSTOVC(share)->vc_flags & SMBV_SMB2) {
ctx->f_infolevel = SMB_FIND_BOTH_DIRECTORY_INFO;
}
else {
ctx->f_infolevel = SMB_FIND_BOTH_DIRECTORY_INFO;
}
}
ctx->f_attrmask = SMB_EFA_SYSTEM | SMB_EFA_HIDDEN | SMB_EFA_DIRECTORY;
ctx->f_lookupName = lookupName;
ctx->f_lookupNameLen = lookupNameLen;
ctx->f_MaxNetworkNameBufferSize = share->ss_maxfilenamelen * 4;
SMB_MALLOC(ctx->f_NetworkNameBuffer, char *, ctx->f_MaxNetworkNameBufferSize, M_TEMP, M_WAITOK);
if (ctx->f_NetworkNameBuffer == NULL) {
SMBERROR("f_NetworkNameBuffer failed\n");
error = ENOMEM;
}
if (error) {
smbfs_smb_findclose(ctx, context);
}
else {
*ctxpp = ctx;
}
return error;
}
int
smbfs_findnext(struct smbfs_fctx *ctx, vfs_context_t context)
{
int error;
struct timespec save_reqtime;
for (;;) {
save_reqtime = ctx->f_attr.fa_reqtime;
bzero(&ctx->f_attr, sizeof(ctx->f_attr));
ctx->f_attr.fa_reqtime = save_reqtime;
error = smbfs_smb_findnext(ctx, context);
if (error == EAGAIN) {
save_reqtime = ctx->f_attr.fa_reqtime;
bzero(&ctx->f_attr, sizeof(ctx->f_attr));
ctx->f_attr.fa_reqtime = save_reqtime;
error = smbfs_smb_findnext(ctx, context);
}
if (error) {
return error;
}
if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_share))) {
if ((ctx->f_NetworkNameLen == 2 &&
letohs(*(uint16_t *)ctx->f_NetworkNameBuffer) == 0x002e) ||
(ctx->f_NetworkNameLen == 4 &&
letohl(*(uint32_t *)ctx->f_NetworkNameBuffer) == 0x002e002e))
continue;
}
else {
if ((ctx->f_NetworkNameLen == 1 && ctx->f_NetworkNameBuffer[0] == '.') ||
(ctx->f_NetworkNameLen == 2 && ctx->f_NetworkNameBuffer[0] == '.' &&
ctx->f_NetworkNameBuffer[1] == '.'))
continue;
}
break;
}
if (ctx->f_LocalName) {
SMB_FREE(ctx->f_LocalName, M_TEMP);
}
ctx->f_LocalNameLen = ctx->f_NetworkNameLen;
ctx->f_LocalName = smbfs_ntwrkname_tolocal(ctx->f_NetworkNameBuffer,
&ctx->f_LocalNameLen,
SMB_UNICODE_STRINGS(SSTOVC(ctx->f_share)));
if (!(SSTOVC(ctx->f_share)->vc_misc_flags & SMBV_HAS_FILEIDS)) {
ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp,
ctx->f_LocalName,
ctx->f_LocalNameLen);
}
return 0;
}
int
smbfs_lookup(struct smb_share *share, struct smbnode *dnp, const char **namep,
size_t *nmlenp, struct smbfattr *fap, vfs_context_t context)
{
struct smbfs_fctx *ctx;
int error = EINVAL;
const char *name = (namep ? *namep : NULL);
size_t nmlen = (nmlenp ? *nmlenp : 0);
struct smbmount *smp;
struct smb_vc *vcp = SSTOVC(share);
enum vtype vnode_type = VREG;
DBG_ASSERT(dnp);
SMB_LOG_KTRACE(SMB_DBG_SMBFS_LOOKUP | DBG_FUNC_START, 0, 0, 0, 0, 0);
if (dnp == NULL) {
SMBERROR("The parent node is NULL, shouldn't happen\n");
error = EINVAL;
goto done;
}
smp = dnp->n_mount;
if ((dnp->n_ino == smp->sm_root_ino) && (name == NULL)) {
uint64_t DIFF1980TO1601 = 11960035200ULL*10000000ULL;
bzero(fap, sizeof(*fap));
nanouptime(&fap->fa_reqtime);
fap->fa_attr = SMB_EFA_DIRECTORY;
fap->fa_vtype = VDIR;
fap->fa_valid_mask |= FA_VTYPE_VALID;
if (smp->sm_root_ino == 0) {
smp->sm_root_ino = SMBFS_ROOT_INO;
fap->fa_ino = SMBFS_ROOT_INO;
}
else {
fap->fa_ino = smp->sm_root_ino;
}
vnode_type = VDIR;
if (!(vcp->vc_misc_flags & SMBV_NO_QUERYINFO)) {
if (UNIX_CAPS(share) & UNIX_QFILEINFO_UNIX_INFO2_CAP) {
error = smbfs_smb_qpathinfo(share, dnp, vnode_type,
fap, SMB_QFILEINFO_UNIX_INFO2,
NULL, NULL,
context);
}
else {
error = smbfs_smb_qpathinfo(share, dnp, vnode_type,
fap, SMB_QFILEINFO_ALL_INFO,
NULL, NULL,
context);
}
SMB_LOG_KTRACE(SMB_DBG_SMBFS_LOOKUP | DBG_FUNC_NONE,
0xabc001, error, 0, 0, 0);
if (error) {
SMBERROR("Server failed Query Info %d\n", error);
vcp->vc_misc_flags |= SMBV_NO_QUERYINFO;
}
}
if ((vcp->vc_flags & SMBV_SMB2) &&
(vcp->vc_misc_flags & SMBV_NO_QUERYINFO)) {
error = smb2fs_smb_cmpd_query_dir_one(share, dnp,
NULL, 0,
fap, NULL, NULL,
context);
SMB_LOG_KTRACE(SMB_DBG_SMBFS_LOOKUP | DBG_FUNC_NONE,
0xabc002, error, 0, 0, 0);
}
if (fap->fa_mtime.tv_sec == 0)
smb_time_NT2local(DIFF1980TO1601, &fap->fa_mtime);
if (fap->fa_crtime.tv_sec == 0)
smb_time_NT2local(DIFF1980TO1601, &fap->fa_crtime);
if (fap->fa_atime.tv_sec == 0)
fap->fa_atime = fap->fa_mtime;
if (fap->fa_chtime.tv_sec == 0)
fap->fa_chtime = fap->fa_mtime;
goto done;
}
if (nmlen == 1 && name && name[0] == '.') {
error = smbfs_lookup(share, dnp, NULL, NULL, fap, context);
SMB_LOG_KTRACE(SMB_DBG_SMBFS_LOOKUP | DBG_FUNC_NONE,
0xabc003, error, 0, 0, 0);
goto done;
} else if (nmlen == 2 && name && name[0] == '.' && name[1] == '.') {
lck_rw_lock_shared(&dnp->n_parent_rwlock);
if (dnp->n_parent) {
error = smbfs_lookup(share, dnp->n_parent, NULL, NULL, fap, context);
SMB_LOG_KTRACE(SMB_DBG_SMBFS_LOOKUP | DBG_FUNC_NONE,
0xabc004, error, 0, 0, 0);
}
lck_rw_unlock_shared(&dnp->n_parent_rwlock);
goto done;
}
bzero(fap, sizeof(*fap));
nanouptime(&fap->fa_reqtime);
if ((dnp->n_vnode) && vnode_isnamedstream(dnp->n_vnode)) {
goto doQueryInfo;
}
if (vcp->vc_flags & SMBV_SMB2) {
error = smb2fs_smb_cmpd_query_dir_one(share, dnp,
name, nmlen,
fap, (char **) namep, nmlenp,
context);
SMB_LOG_KTRACE(SMB_DBG_SMBFS_LOOKUP | DBG_FUNC_NONE,
0xabc005, error, 0, 0, 0);
}
else {
Boolean unix_symlink = ((UNIX_CAPS(share) & UNIX_SFILEINFO_UNIX_LINK_CAP)) ? TRUE : FALSE;
Boolean darwin = (SSTOVC(share)->vc_flags & SMBV_DARWIN) ? TRUE : FALSE;
if (unix_symlink && !darwin && (namep == NULL) && dnp->n_vnode &&
(vnode_vtype(dnp->n_vnode) == VLNK)) {
return smbfs_smb_qpathinfo(share, dnp, VREG,
fap, SMB_QFILEINFO_UNIX_INFO2,
namep, nmlenp,
context);
}
error = smbfs_smb_findopen(share, dnp, name, nmlen, &ctx, FALSE, context);
if (error) {
goto done;
}
error = smbfs_findnext(ctx, context);
if (error == 0) {
*fap = ctx->f_attr;
if (name == NULL) {
fap->fa_ino = dnp->n_ino;
}
if (namep) {
*namep = ctx->f_LocalName;
ctx->f_LocalName = NULL;
}
if (nmlenp) {
*nmlenp = ctx->f_LocalNameLen;
}
}
smbfs_smb_findclose(ctx, context);
if ((error == ENOENT) && unix_symlink && !darwin && (namep)) {
SMBWARNING("Working around Samba bug doing second lookup\n");
vnode_type = (ctx->f_attr.fa_attr & SMB_EFA_DIRECTORY) ? VDIR : VREG;
error = smbfs_smb_qpathinfo(share, dnp, vnode_type,
fap, SMB_QFILEINFO_UNIX_INFO2,
namep, nmlenp,
context);
goto done;
}
}
if ((error != EACCES) && (error != EPERM) && (error != EAGAIN)) {
goto done;
}
doQueryInfo:
vnode_type = VREG;
if (UNIX_CAPS(share) & UNIX_QFILEINFO_UNIX_INFO2_CAP) {
if (namep == NULL) {
error = smbfs_smb_qpathinfo(share, dnp, vnode_type,
fap, SMB_QFILEINFO_UNIX_INFO2,
namep, nmlenp,
context);
} else {
error = EACCES;
}
}
else {
error = smbfs_smb_qpathinfo(share, dnp, vnode_type,
fap, SMB_QFILEINFO_ALL_INFO,
namep, nmlenp,
context);
}
SMB_LOG_KTRACE(SMB_DBG_SMBFS_LOOKUP | DBG_FUNC_NONE,
0xabc006, error, 0, 0, 0);
done:
SMB_LOG_KTRACE(SMB_DBG_SMBFS_LOOKUP | DBG_FUNC_END, error, 0, 0, 0, 0);
return error;
}
void smbfs_closedirlookup(struct smbnode *np, vfs_context_t context)
{
if (np->d_fctx)
smbfs_smb_findclose(np->d_fctx, context);
np->d_fctx = NULL;
np->d_offset = 0;
if (np->d_nextEntry)
SMB_FREE(np->d_nextEntry, M_TEMP);
np->d_nextEntry = NULL;
np->d_nextEntryLen = 0;
}
static int
smbfs_smb_getsec_int(struct smb_share *share, SMBFID fid, uint32_t selector,
struct ntsecdesc **res, uint32_t *reslen,
vfs_context_t context)
{
struct smb_ntrq *ntp;
struct mbchain *mbp;
struct mdchain *mdp;
int error;
size_t len;
uint16_t smb1_fid = (uint16_t) fid;
error = smb_nt_alloc(SSTOCP(share), NT_TRANSACT_QUERY_SECURITY_DESC, context, &ntp);
if (error)
return error;
mbp = &ntp->nt_tparam;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, selector);
ntp->nt_maxpcount = 4;
ntp->nt_maxdcount = *reslen;
error = smb_nt_request(ntp);
*res = NULL;
mdp = &ntp->nt_rparam;
if (error) {
if (ntp->nt_flags & SMBT2_MOREDATA)
md_get_uint32le(mdp, reslen);
goto done;
} else {
md_get_uint32le(mdp, reslen);
}
mdp = &ntp->nt_rdata;
if (mdp->md_top) {
len = m_fixhdr(mdp->md_top);
if (len != (size_t)*reslen)
SMBWARNING("Sent us %ld but said they sent us %d for fid = 0x%x\n",
len, *reslen, letohs(smb1_fid));
if (len < (size_t)*reslen) {
*reslen = (uint32_t)len;
} else if (len > (size_t)*reslen) {
len = *reslen;
}
if (len >= sizeof(struct ntsecdesc)) {
SMB_MALLOC(*res, struct ntsecdesc *, len, M_TEMP, M_WAITOK);
md_get_mem(mdp, (caddr_t)*res, len, MB_MSYSTEM);
} else {
SMBERROR("len %ld < ntsecdesc %ld fid 0x%x\n", len,
sizeof(struct ntsecdesc), letohs(smb1_fid));
error = EBADRPC;
}
} else {
SMBERROR("null md_top? fid 0x%x\n", letohs(smb1_fid));
error = EBADRPC;
}
done:
smb_nt_done(ntp);
return (error);
}
int
smbfs_smb_getsec(struct smb_share *share, struct smbnode *np,
uint32_t desired_access, SMBFID fid, uint32_t selector,
struct ntsecdesc **res, size_t *rt_len, vfs_context_t context)
{
int error;
uint32_t seclen;
seclen = SSTOVC(share)->vc_txmax;
if (SSTOVC(share)->vc_flags & SMBV_SMB2) {
error = smb2fs_smb_security_get(share, np,
desired_access, selector,
res, &seclen,
context);
}
else {
error = smbfs_smb_getsec_int(share, fid, selector, res, &seclen, context);
if (error && (seclen > SSTOVC(share)->vc_txmax)) {
error = smbfs_smb_getsec_int(share, fid, selector, res, &seclen,
context);
}
}
if (error == 0)
*rt_len = seclen;
return (error);
}
int
smb1fs_setsec(struct smb_share *share, SMBFID fid, uint32_t selector,
uint16_t ControlFlags, struct ntsid *owner, struct ntsid *group,
struct ntacl *sacl, struct ntacl *dacl, vfs_context_t context)
{
struct smb_ntrq *ntp;
struct mbchain *mbp;
int error;
uint32_t off;
struct ntsecdesc ntsd;
uint16_t smb1_fid = (uint16_t) fid;
error = smb_nt_alloc(SSTOCP(share), NT_TRANSACT_SET_SECURITY_DESC,
context, &ntp);
if (error)
return error;
mbp = &ntp->nt_tparam;
mb_init(mbp);
mb_put_mem(mbp, (caddr_t)&smb1_fid, sizeof(smb1_fid), MB_MSYSTEM);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, selector);
mbp = &ntp->nt_tdata;
mb_init(mbp);
bzero(&ntsd, sizeof ntsd);
ntsd.Revision = 0x01;
ControlFlags |= SE_SELF_RELATIVE;
off = (uint32_t)sizeof(ntsd);
if (owner) {
ntsd.OffsetOwner = htolel(off);
off += (uint32_t)sidlen(owner);
}
if (group) {
ntsd.OffsetGroup = htolel(off);
off += (uint32_t)sidlen(group);
}
if (sacl) {
ControlFlags |= SE_SACL_PRESENT | SE_SACL_AUTO_INHERITED | SE_SACL_AUTO_INHERIT_REQ;
ntsd.OffsetSacl = htolel(off);
off += acllen(sacl);
}
if (dacl) {
ControlFlags |= SE_DACL_PRESENT | SE_DACL_AUTO_INHERITED | SE_DACL_AUTO_INHERIT_REQ;
ntsd.OffsetDacl = htolel(off);
}
ntsd.ControlFlags = htoles(ControlFlags);
mb_put_mem(mbp, (caddr_t)&ntsd, sizeof ntsd, MB_MSYSTEM);
if (owner)
mb_put_mem(mbp, (caddr_t)owner, sidlen(owner), MB_MSYSTEM);
if (group)
mb_put_mem(mbp, (caddr_t)group, sidlen(group), MB_MSYSTEM);
if (sacl)
mb_put_mem(mbp, (caddr_t)sacl, acllen(sacl), MB_MSYSTEM);
if (dacl)
mb_put_mem(mbp, (caddr_t)dacl, acllen(dacl), MB_MSYSTEM);
ntp->nt_maxpcount = 0;
ntp->nt_maxdcount = 0;
error = smb_nt_request(ntp);
if ((error != 0) && (ntp->nt_status == STATUS_INVALID_SID)) {
error = 0;
}
smb_nt_done(ntp);
return (error);
}