#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/xattr.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/mount.h>
#include <sys/unistd.h>
#include <sys/lockf.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <vfs/vfs_support.h>
#include <sys/namei.h>
#include <libkern/OSAtomic.h>
#include <sys/attr.h>
#include <sys/kauth.h>
#include <sys/syslog.h>
#include <sys/smb_apple.h>
#include <sys/smb_byte_order.h>
#include <sys/mchain.h>
#include <sys/msfscc.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
#include <smbfs/smbfs_lockf.h>
#include <netsmb/smb_converter.h>
#include <smbfs/smbfs_security.h>
#include <sys/buf.h>
char smb_symmagic[SMB_SYMMAGICLEN] = {'X', 'S', 'y', 'm', '\n'};
static int smbfs_setattr(struct smb_share *share, vnode_t vp, struct vnode_attr *vap,
vfs_context_t context);
static void smbfs_set_create_vap(struct smb_share *share, struct vnode_attr *vap, vnode_t vp,
vfs_context_t context, int set_mode_now);
static int smbfs_vnop_compound_open(struct vnop_compound_open_args *ap);
static int smbfs_vnop_open(struct vnop_open_args *ap);
static int
smbfs_io_reopen(struct smb_share *share, vnode_t vp, uio_t uio,
uint16_t accessMode, uint16_t *fid, int error,
vfs_context_t context)
{
struct smbnode *np = VTOSMB(vp);
if (np->f_openState != kNeedReopen)
return(error);
error = smbfs_smb_reopen_file(share, np, context);
if (error)
return(error);
if (FindFileRef(vp, vfs_context_proc(context), accessMode, kCheckDenyOrLocks,
uio_offset(uio), uio_resid(uio), NULL, fid)) {
*fid = np->f_fid;
}
DBG_ASSERT(*fid);
return(0);
}
int
smbfs_update_cache(struct smb_share *share, vnode_t vp,
struct vnode_attr *vap, vfs_context_t context)
{
int useCacheData = (share->ss_flags & SMBS_RECONNECTING);
struct smbfattr fattr;
int error = smbfs_attr_cachelookup(share, vp, vap, context, useCacheData);
if (error != ENOENT)
return (error);
error = smbfs_lookup(share, VTOSMB(vp), NULL, NULL, &fattr, context);
if (error)
return (error);
smbfs_attr_cacheenter(share, vp, &fattr, TRUE, context);
return (smbfs_attr_cachelookup(share, vp, vap, context, useCacheData));
}
int
smbfs_close(struct smb_share *share, vnode_t vp, int openMode,
vfs_context_t context)
{
struct smbnode *np = VTOSMB(vp);
proc_t p = vfs_context_proc(context);
struct fileRefEntry *fndEntry = NULL;
struct fileRefEntry *curr = NULL;
int error = 0;
uint16_t accessMode = 0;
uint16_t fid = 0;
int32_t needOpenFile;
uint16_t openAccessMode;
uint32_t rights;
if ((np->f_refcnt > 1) && (smbfs_smb_reopen_file(share, np, context) == EIO)) {
SMBDEBUG(" %s waiting to be revoked\n", np->n_name);
np->f_refcnt--;
return (0);
}
if (openMode & FREAD)
accessMode |= kAccessRead;
if (openMode & FWRITE)
accessMode |= kAccessWrite;
if (np->f_refcnt == 1) {
ubc_msync(vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC | UBC_INVALIDATE);
if (np->f_fid) {
uint16_t oldFID = np->f_fid;
np->f_fid = 0;
np->f_accessMode = 0;
np->f_rights = 0;
np->f_openRWCnt = 0;
np->f_openRCnt = 0;
np->f_openWCnt = 0;
np->f_openTotalWCnt = 0;
np->f_needClose = 0;
np->f_clusterCloseError = 0;
if (np->f_smbflock) {
SMB_FREE(np->f_smbflock, M_LOCKF);
np->f_smbflock = NULL;
}
error = smbfs_smb_close(share, oldFID, context);
if (error)
SMBWARNING("close file failed %d on fid %d\n", error, oldFID);
}
lck_mtx_lock(&np->f_openDenyListLock);
curr = np->f_openDenyList;
while (curr != NULL) {
error = smbfs_smb_close(share, curr->fid, context);
if (error)
SMBWARNING("close file failed %d on fid %d\n", error, curr->fid);
curr = curr->next;
}
lck_mtx_unlock(&np->f_openDenyListLock);
np->f_refcnt = 0;
RemoveFileRef (vp, NULL);
if (np->n_flag & NDELETEONCLOSE) {
if (smbfs_smb_delete(share, np, NULL, 0, 0, context) == 0)
np->n_flag &= ~NDELETEONCLOSE;
}
if (vnode_isnocache(vp))
vnode_clearnocache(vp);
if (np->n_flag & NATTRCHANGED)
np->attribute_cache_timer = 0;
lck_mtx_lock(&np->f_openStateLock);
if (np->f_openState == kNeedRevoke)
error = 0;
np->f_openState = 0;
lck_mtx_unlock(&np->f_openStateLock);
goto exit;
}
if (openMode & FHASLOCK) {
uint16_t tempAccessMode = accessMode;
tempAccessMode |= kDenyWrite;
error = FindFileRef(vp, p, tempAccessMode, kExactMatch, 0, 0, &fndEntry,
&fid);
if (error != 0) {
tempAccessMode |= kDenyRead;
error = FindFileRef(vp, p, tempAccessMode, kExactMatch, 0, 0,
&fndEntry, &fid);
}
if (error == 0)
accessMode = tempAccessMode;
} else {
error = FindFileRef(vp, p, accessMode, kExactMatch, 0, 0, &fndEntry, &fid);
}
np->f_refcnt--;
if ((error == 0) && fndEntry && (fndEntry->refcnt > 0)) {
fndEntry->refcnt--;
goto exit;
}
if ((error == 0) && fndEntry) {
error = smbfs_smb_close(share, fndEntry->fid, context);
RemoveFileRef(vp, fndEntry);
goto exit;
}
needOpenFile = 0;
openAccessMode = 0;
fid = 0;
rights = SMB2_READ_CONTROL;
error = 0;
switch (accessMode) {
case (kAccessRead | kAccessWrite):
np->f_openRWCnt -= 1;
if ((np->f_openRWCnt == 0) && (np->f_openRCnt > 0) && (np->f_openWCnt == 0)) {
needOpenFile = 1;
openAccessMode = kAccessRead;
rights |= SMB2_FILE_READ_DATA;
}
break;
case kAccessRead:
np->f_openRCnt -= 1;
break;
case kAccessWrite:
np->f_openWCnt -= 1;
if ( (np->f_openRCnt > 0) && (np->f_openRWCnt == 0) &&
(np->f_openWCnt == 0) ) {
needOpenFile = 1;
openAccessMode = kAccessRead;
rights |= SMB2_FILE_READ_DATA;
}
break;
}
if (needOpenFile == 1) {
error = smbfs_smb_open(share, np, rights, NTCREATEX_SHARE_ACCESS_ALL,
&fid, context);
if (error == 0) {
uint16_t oldFID = np->f_fid;
ubc_msync(vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC | UBC_INVALIDATE);
np->f_fid = fid;
np->f_accessMode = openAccessMode;
np->f_rights = rights;
error = smbfs_smb_close(share, oldFID, context);
if (error)
SMBWARNING("close file failed %d on fid %d\n", error, oldFID);
}
}
exit:
if ((error == 0) && (openMode & FWRITE) && (np->f_openTotalWCnt > 0)) {
np->f_openTotalWCnt--;
}
return (error);
}
static int
smbfs_vnop_close(struct vnop_close_args *ap)
{
vnode_t vp = ap->a_vp;
int error = 0;
struct smbnode *np;
if (smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK) != 0)
return (0);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_close;
if (vnode_isdir(vp)) {
if (--np->d_refcnt) {
error = 0;
} else {
smbfs_closedirlookup(np, ap->a_context);
}
} else if ( vnode_isreg(vp) ) {
int clusterCloseError = np->f_clusterCloseError;
struct smb_share *share;
if (!vnode_vfsisrdonly(vp) && smbfsIsCacheable(vp)) {
if (np->n_flag & NISMAPPED) {
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC);
} else {
cluster_push(vp, IO_CLOSE);
}
}
share = smb_get_share_with_reference(VTOSMBFS(vp));
error = smbfs_close(share, vp, ap->a_fflag, ap->a_context);
smb_share_rele(share, ap->a_context);
if (!error)
error = clusterCloseError;
}
smbnode_unlock(np);
return (error);
}
static void
smbfs_get_rights_shareMode(int fmode, uint32_t *rights, uint32_t *shareMode, uint16_t *accessMode)
{
*rights = SMB2_READ_CONTROL;
if (fmode & FREAD) {
*accessMode |= kAccessRead;
*rights |= SMB2_FILE_READ_DATA;
}
if (fmode & FWRITE) {
*accessMode |= kAccessWrite;
*rights |= SMB2_FILE_APPEND_DATA | SMB2_FILE_WRITE_DATA;
}
*shareMode = NTCREATEX_SHARE_ACCESS_ALL;
if (fmode & O_SHLOCK) {
*accessMode |= kDenyWrite;
*shareMode &= ~NTCREATEX_SHARE_ACCESS_WRITE;
}
if (fmode & O_EXLOCK) {
*accessMode |= kDenyWrite;
*accessMode |= kDenyRead;
*shareMode &= ~(NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_READ);
}
}
static void
smbfs_update_RW_cnts(vnode_t vp, uint16_t accessMode)
{
struct smbnode *np = VTOSMB(vp);
switch (accessMode) {
case (kAccessWrite | kAccessRead):
np->f_openRWCnt += 1;
break;
case kAccessRead:
np->f_openRCnt += 1;
break;
case kAccessWrite:
np->f_openWCnt += 1;
break;
}
}
static int
smbfs_create_open(struct smb_share *share, vnode_t dvp, struct componentname *cnp,
struct vnode_attr *vap, uint32_t open_disp, int fmode,
uint16_t *fidp, struct smbfattr *fattrp, vnode_t *vpp,
vfs_context_t context)
{
struct smbnode *dnp = VTOSMB(dvp);
struct smbmount *smp = VTOSMBFS(dvp);
vnode_t vp;
const char *name = cnp->cn_nameptr;
size_t nmlen = cnp->cn_namelen;
int error = 0;
uint32_t rights, addedReadRights;
uint16_t accessMode = 0;
uint16_t savedAccessMode = 0;
struct smbnode *np;
uint32_t shareMode = 0;
char *target = NULL;
size_t targetlen = 0;
*fidp = 0;
smbfs_get_rights_shareMode(fmode, &rights, &shareMode, &accessMode);
savedAccessMode = accessMode;
addedReadRights = (accessMode == kAccessWrite) ? SMB2_FILE_READ_DATA : 0;
error = smbfs_smb_ntcreatex(share, dnp, rights | addedReadRights, shareMode,
VREG, fidp, name, nmlen, open_disp, 0, fattrp,
TRUE, context);
if (error && addedReadRights) {
addedReadRights = 0;
error = smbfs_smb_ntcreatex(share, dnp, rights, shareMode, VREG, fidp,
name, nmlen, open_disp, 0, fattrp, TRUE,
context);
}
if (error) {
return error;
}
if (fattrp->fa_attr & SMB_EFA_REPARSE_POINT) {
error = smbfs_smb_get_reparse_tag(share, *fidp, &fattrp->fa_reparse_tag,
&target, &targetlen, context);
if (error) {
(void) smbfs_smb_close(share, *fidp, context);
return error;
}
}
error = smbfs_nget(share, vnode_mount(dvp), dvp, name, nmlen, fattrp, &vp,
cnp->cn_flags, context);
if (error) {
if (target) {
SMB_FREE(target, M_TEMP);
}
(void) smbfs_smb_close(share, *fidp, context);
return error;
}
if (*vpp) {
vnode_put(*vpp);
}
*vpp = vp;
np = VTOSMB(vp);
if (vnode_isdir(vp)) {
np->d_refcnt++;
(void)smbfs_smb_close(share, *fidp, context);
} else if (vnode_islnk(vp)) {
(void)smbfs_smb_close(share, *fidp, context);
if (target && targetlen) {
smbfs_update_symlink_cache(np, target, targetlen);
}
} else if (vnode_isreg(vp)) {
np->f_refcnt++;
np->f_fid = *fidp;
np->f_rights = rights;
np->f_accessMode = accessMode;
if (addedReadRights) {
np->f_rights |= SMB2_FILE_READ_DATA;
np->f_accessMode |= kAccessRead;
}
if ((fmode & O_EXLOCK) || (fmode & O_SHLOCK)) {
AddFileRef (vp, vfs_context_proc(context), accessMode, rights, *fidp, NULL);
} else {
smbfs_update_RW_cnts(vp, savedAccessMode);
if (accessMode == kAccessWrite) {
vnode_setnocache(vp);
}
}
}
if (target) {
SMB_FREE(target, M_TEMP);
}
if (fattrp->fa_created_disp == FILE_CREATE) {
struct timespec ts;
nanouptime(&ts);
VTOSMB(vp)->finfo_cache = ts.tv_sec;
VTOSMB(vp)->rfrk_cache_timer = ts.tv_sec;
if (vap != NULL) {
smbfs_set_create_vap(share, vap, vp, context, TRUE);
}
smbfs_attr_touchdir(dnp, (share->ss_fstype == SMB_FS_FAT));
if (dnp->n_flag & NNEGNCENTRIES) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(dvp);
}
smp->sm_statfstime = 0;
}
smbnode_unlock(VTOSMB(vp));
return (error);
}
int
smbfs_open(struct smb_share *share, vnode_t vp, int mode,
vfs_context_t context)
{
proc_t p = vfs_context_proc(context);
struct smbnode *np = VTOSMB(vp);
uint16_t accessMode = 0;
uint16_t savedAccessMode = 0;
uint32_t rights;
uint32_t shareMode;
uint16_t fid;
int error = 0;
int warning = 0;
if ((np->f_refcnt) &&
((error = smbfs_smb_reopen_file(share, np, context)) != 0)) {
SMBDEBUG(" %s waiting to be revoked\n", np->n_name);
return (error);
}
smbfs_get_rights_shareMode(mode, &rights, &shareMode, &accessMode);
savedAccessMode = accessMode;
if ((mode & O_EXLOCK) || (mode & O_SHLOCK)) {
struct fileRefEntry *fndEntry = NULL;
if (np->f_needClose) {
np->f_needClose = 0;
warning = smbfs_close(share, vp, FREAD, context);
if (warning)
SMBWARNING("error %d closing %s\n", warning, np->n_name);
}
if ((accessMode & kAccessRead) && !(accessMode & kAccessWrite)) {
error = FindFileRef(vp, p, kAccessRead, kAnyMatch, 0, 0, &fndEntry, &fid);
if ((error == 0) && fndEntry) {
DBG_ASSERT(fndEntry);
fndEntry->refcnt++;
goto exit;
}
}
fndEntry = NULL;
error = FindFileRef(vp, p, accessMode, kExactMatch, 0, 0, &fndEntry, &fid);
if (error == 0) {
if ((mode & O_EXLOCK) || ((accessMode & kDenyWrite) &&
(accessMode & kAccessWrite)))
error = EBUSY;
else {
DBG_ASSERT(fndEntry);
fndEntry->refcnt++;
}
} else {
error = smbfs_smb_open(share, np, rights, shareMode, &fid, context);
if (error == 0) {
AddFileRef (vp, p, accessMode, rights, fid, NULL);
}
}
goto exit;
}
if (np->f_fid) {
int needUpgrade = 0;
switch (np->f_accessMode) {
case (kAccessRead | kAccessWrite):
break;
case kAccessRead:
if (accessMode & kAccessWrite) {
needUpgrade = 1;
accessMode |= kAccessRead;
rights |= SMB2_FILE_READ_DATA;
}
break;
case kAccessWrite:
if (accessMode & kAccessRead) {
needUpgrade = 1;
accessMode |= kAccessWrite;
rights |= SMB2_FILE_APPEND_DATA | SMB2_FILE_WRITE_DATA;
}
break;
}
if (! needUpgrade)
goto ShareOpen;
} else if (accessMode == kAccessWrite) {
error = smbfs_smb_open(share, np, rights | SMB2_FILE_READ_DATA,
shareMode, &fid, context);
if (error == 0) {
np->f_fid = fid;
np->f_rights = rights | SMB2_FILE_READ_DATA;
np->f_accessMode = accessMode | kAccessRead;
goto ShareOpen;
}
}
error = smbfs_smb_open(share, np, rights, shareMode, &fid, context);
if (error)
goto exit;
if (np->f_refcnt && np->f_fid) {
warning = smbfs_smb_close(share, np->f_fid, context);
if (warning)
SMBWARNING("error %d closing %s\n", warning, np->n_name);
}
np->f_fid = fid;
np->f_rights = rights;
np->f_accessMode = accessMode;
ShareOpen:
smbfs_update_RW_cnts(vp, savedAccessMode);
if (accessMode == kAccessWrite) {
vnode_setnocache(vp);
}
exit:
if (!error) {
np->f_refcnt++;
if (mode & FWRITE) {
np->f_openTotalWCnt++;
}
}
return (error);
}
static int
smbfs_vnop_open_common(vnode_t vp, int mode, vfs_context_t context, void *n_lastvop)
{
struct smbnode *np;
int error;
if (!vnode_isreg(vp) && !vnode_isdir(vp))
return (EACCES);
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = n_lastvop;
if (vnode_isdir(vp)) {
np->d_refcnt++;
error = 0;
} else {
struct smb_share * share;
share = smb_get_share_with_reference(VTOSMBFS(vp));
error = smbfs_open(share, vp, mode, context);
if ((error == 0) && (np->set_create_va_mode) && (mode & O_CREAT)) {
struct vnode_attr vap;
np->set_create_va_mode = FALSE;
VATTR_INIT(&vap);
VATTR_SET_ACTIVE(&vap, va_mode);
vap.va_mode = np->create_va_mode;
error = smbfs_setattr(share, vp, &vap, context);
if (error)
(void)smbfs_close(share, vp, mode, context);
}
smb_share_rele(share, context);
}
if (error == EBUSY)
error = EAGAIN;
smbnode_unlock(np);
return(error);
}
static int
smbfs_vnop_compound_open(struct vnop_compound_open_args *ap)
{
vnode_t dvp = ap->a_dvp;
vnode_t *vpp = ap->a_vpp;
vnode_t vp = (ap->a_vpp) ? *ap->a_vpp : NULL;
vfs_context_t context = ap->a_context;
struct componentname *cnp = ap->a_cnp;
struct vnode_attr *vap = ap->a_vap;
int fmode = ap->a_fmode;
int error;
int create_authorizer_error = 0;
uint32_t open_disp = 0;
struct smb_share *share = NULL;
struct smbnode *dnp;
uint16_t fid = 0;
struct smbfattr fattr;
uint32_t vid;
if (vpp == NULL) {
SMBWARNING("Calling us without a vpp\n");
return ENOTSUP;
}
if ((fmode & O_CREAT) && ((vap == NULL) || (vap->va_type != VREG))) {
return (ENOTSUP);
}
share = smb_get_share_with_reference(VTOSMBFS(dvp));
if (vp == NULLVP) {
if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
vid = vnode_vid(dvp);
error = vnode_getwithvid(dvp, vid);
if (error) {
goto done;
}
vp = dvp;
} else if (smbfs_nget(share, vnode_mount(dvp), dvp, cnp->cn_nameptr,
cnp->cn_namelen, NULL, &vp, cnp->cn_flags,
ap->a_context) == 0) {
smbnode_unlock(VTOSMB(vp));
}
}
if (vp && (vnode_islnk(vp) || (VTOSMB(vp)->n_dosattr & SMB_EFA_REPARSE_POINT))) {
SMBDEBUG("symlink %s\n", VTOSMB(*vpp)->n_name);
error = vnode_lookup_continue_needed(vp, cnp);
if (error) {
*vpp = vp;
vp = NULL;
goto done;
}
fmode &= ~(O_CREAT | O_TRUNC);
}
if (vp && (!(fmode & (O_CREAT | O_TRUNC)) || (vnode_isreg(vp) && VTOSMB(vp)->f_refcnt))) {
if ( (fmode & O_EXCL) && vnode_isreg(vp) && VTOSMB(vp)->f_refcnt) {
error = EEXIST;
*vpp = vp;
vp = NULL;
goto done;
}
error = ap->a_open_existing_authorizer(vp, cnp, fmode, context, NULL);
if (!error) {
error = smbfs_vnop_open_common(vp, fmode, context, smbfs_vnop_compound_open);
}
if (!error && (fmode & O_TRUNC)){
struct vnode_attr va;
memset(&va, 0, sizeof(va));
VATTR_INIT(&va);
VATTR_SET_ACTIVE(&va, va_data_size);
error = smbfs_setattr(share, vp, &va, context);
if (error) {
(void)smbfs_close(share, vp, fmode, context);
}
}
*vpp = vp;
vp = NULL;
goto done;
}
create_authorizer_error = 0;
if (fmode & O_TRUNC) {
open_disp = FILE_OVERWRITE;
} else {
open_disp = FILE_OPEN;
}
if (fmode & O_CREAT) {
create_authorizer_error = ap->a_open_create_authorizer(dvp, cnp, vap, context, NULL);
if (!create_authorizer_error) {
if (fmode & O_EXCL) {
open_disp = FILE_CREATE;
} else if (fmode & O_TRUNC) {
open_disp = FILE_OVERWRITE_IF;
} else {
open_disp = FILE_OPEN_IF;
}
}
}
if ((error = smbnode_lock(VTOSMB(dvp), SMBFS_EXCLUSIVE_LOCK))) {
goto done;
}
dnp = VTOSMB(dvp);
dnp->n_lastvop = smbfs_vnop_compound_open;
error = smbfs_create_open(share, dvp, cnp, vap, open_disp, fmode, &fid, &fattr, &vp, context);
smbnode_unlock(dnp);
if (error) {
if (create_authorizer_error) {
error = create_authorizer_error;
}
} else {
if ((fattr.fa_created_disp == FILE_CREATE) && (!(fmode & O_CREAT))) {
fattr.fa_created_disp = FILE_OPEN;
SMBERROR("Server created %s when we only wanted it open, server error\n",
cnp->cn_nameptr);
}
if (fattr.fa_created_disp == FILE_CREATE) {
*ap->a_status = COMPOUND_OPEN_STATUS_DID_CREATE;
} else {
error = ap->a_open_existing_authorizer(vp, cnp, fmode, context, NULL);
if (error) {
(void)smbfs_smb_close(share, fid, context);
}
if (vnode_islnk(vp) || (VTOSMB(vp)->n_dosattr & SMB_EFA_REPARSE_POINT)) {
error = vnode_lookup_continue_needed(vp, cnp);
if (!error && (vnode_islnk(vp))) {
error = EACCES;
}
}
}
*vpp = vp;
vp = NULL;
}
done:
if (error) {
if ((fmode & O_CREAT) && (error == ENOENT)) {
SMBDEBUG("Creating %s returned ENOENT, resetting to EACCES\n", cnp->cn_nameptr);
error = EACCES;
} else if ((fmode & O_EXCL) && (error != EEXIST)) {
if ( (smbfs_smb_query_info(share, VTOSMB(dvp), cnp->cn_nameptr, cnp->cn_namelen, NULL, context) == 0)) {
SMBDEBUG("%s: O_EXCL but error = %d, resetting to EEXIST.\n", __FUNCTION__, error);
error = EEXIST;
}
}
}
if (share) {
smb_share_rele(share, context);
}
if (error && (*vpp == NULLVP) && vp) {
vnode_put(vp);
}
return (error);
}
static int
smbfs_vnop_open(struct vnop_open_args *ap)
{
return ( smbfs_vnop_open_common(ap->a_vp, ap->a_mode, ap->a_context, smbfs_vnop_open) );
}
static int
smbfs_vnop_mmap(struct vnop_mmap_args *ap)
{
vnode_t vp = ap->a_vp;
struct smbnode *np = NULL;
int error = 0;
uint32_t mode = (ap->a_fflags & PROT_WRITE) ? (FWRITE | FREAD) : FREAD;
int accessMode = (ap->a_fflags & PROT_WRITE) ? kAccessWrite : kAccessRead;
uint16_t fid;
struct fileRefEntry *entry = NULL;
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (EPERM);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_mmap;
if (np->n_flag & NISMAPPED)
goto out;
if (np->f_fid && (np->f_accessMode & accessMode)) {
np->f_refcnt++;
} else if (FindFileRef(vp, vfs_context_proc(ap->a_context), accessMode,
kAnyMatch, 0, 0, &entry, &fid) == 0) {
entry->refcnt++;
entry->mmapped = TRUE;
np->f_refcnt++;
} else {
SMBERROR("%s We could not find an open file with mode = 0x%x? \n", np->n_name, mode);
error = EPERM;
goto out;
}
np->n_flag |= NISMAPPED;
np->f_mmapMode = mode;
out:
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_mnomap(struct vnop_mnomap_args *ap)
{
vnode_t vp = ap->a_vp;
struct smbnode *np;
struct fileRefEntry *entry;
int error = 0;
if (smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK))
return (EPERM);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_mnomap;
if (np->f_refcnt == 1) {
struct smb_share *share;
share = smb_get_share_with_reference(VTOSMBFS(vp));
error = smbfs_close(share, vp, np->f_mmapMode, ap->a_context);
smb_share_rele(share, ap->a_context);
if (error)
SMBWARNING("%s close failed with error = %d\n", np->n_name, error);
goto out;
} else {
np->f_refcnt--;
}
if (FindMappedFileRef(vp, &entry, NULL) == TRUE) {
entry->mmapped = FALSE;
if (entry->refcnt > 0)
entry->refcnt--;
else
RemoveFileRef(vp, entry);
}
out:
np->f_mmapMode = 0;
np->n_flag &= ~NISMAPPED;
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_inactive(struct vnop_inactive_args *ap)
{
vnode_t vp = ap->a_vp;
struct smbnode *np;
struct smb_share *share = NULL;
int error = 0;
int releaseLock = TRUE;
(void)smbnode_lock(VTOSMB(vp), SMBFS_RECLAIM_LOCK);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_inactive;
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (np->n_symlink_target) {
SMB_FREE(np->n_symlink_target, M_TEMP);
}
np->n_symlink_target_len = 0;
np->n_symlink_cache_timer = 0;
if (!vnode_isnamedstream(vp))
smbfs_clear_acl_cache(np);
if ((vnode_isinuse(vp, 0)) && !(vfs_isforce(vnode_mount(vp))))
goto out;
if (vnode_isdir(vp)) {
smbfs_closedirlookup(np, ap->a_context);
np->d_refcnt = 0;
if (np->d_kqrefcnt) {
smbfs_stop_change_notify(share, np, TRUE, ap->a_context, &releaseLock);
}
goto out;
}
if (np->f_refcnt) {
np->f_refcnt = 1;
error = smbfs_close(share, vp, FREAD, ap->a_context);
if (error) {
SMBDEBUG("error %d closing fid %d file %s\n", error,
np->f_fid, np->n_name);
}
}
if (np->n_flag & NDELETEONCLOSE) {
error = smbfs_smb_delete(share, np, NULL, 0, 0, ap->a_context);
if (error)
SMBWARNING("error %d deleting silly rename file %s\n",
error, np->n_name);
else np->n_flag &= ~NDELETEONCLOSE;
}
#ifdef SMB_DEBUG
DBG_ASSERT((np->f_refcnt == 0));
DBG_ASSERT((np->f_openDenyList == NULL));
DBG_ASSERT((np->f_smbflock == NULL));
#endif // SMB_DEBUG
out:
smb_share_rele(share, ap->a_context);
if (releaseLock)
smbnode_unlock(np);
return (0);
}
static int smbfs_vnop_reclaim(struct vnop_reclaim_args *ap)
{
vnode_t vp = ap->a_vp;
vnode_t dvp;
struct smbnode *np = NULL;
struct smbmount *smp = NULL;
(void) smbnode_lock(VTOSMB(vp), SMBFS_RECLAIM_LOCK);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_reclaim;
smp = VTOSMBFS(vp);
#ifdef SMB_DEBUG
if (vnode_isreg(vp)) {
DBG_ASSERT((np->f_refcnt == 0));
} else if (vnode_isdir(vp)) {
DBG_ASSERT((np->d_kqrefcnt == 0));
DBG_ASSERT((np->d_fctx == NULL));
}
#endif // SMB_DEBUG
lck_mtx_lock(&smp->sm_reclaim_renamelock);
SET(np->n_flag, NTRANSIT);
dvp = ( (np->n_parent && (np->n_flag & NREFPARENT)) &&
((np->n_parent->n_flag & NTRANSIT) != NTRANSIT)) ?
np->n_parent->n_vnode : NULL;
if (dvp != NULL) {
OSDecrementAtomic(&VTOSMB(dvp)->n_child_refcnt);
np->n_parent = NULL;
}
if ( (np->n_child_refcnt) && !(vfs_isforce(vnode_mount(vp)))) {
if (vnode_getname(vp) != NULL) {
SMBERROR("%s: node: %s, n_child_refcnt not zero like it should be: %ld\n",
__FUNCTION__, vnode_getname(vp), (long) np->n_child_refcnt);
} else {
SMBERROR("%s: n_child_refcnt not zero like it should be: %ld\n",
__FUNCTION__, (long) np->n_child_refcnt);
}
}
if (np->n_child_refcnt) {
smbfs_ClearChildren(smp, np);
}
lck_mtx_unlock(&smp->sm_reclaim_renamelock);
smb_vhashrem(np);
cache_purge(vp);
if (smp->sm_rvp == vp) {
SMBVDEBUG("root vnode\n");
smp->sm_rvp = NULL;
}
if (!vnode_isdir(vp)) {
lck_mtx_destroy(&np->f_openDenyListLock, smbfs_mutex_group);
lck_mtx_destroy(&np->f_openStateLock, smbfs_mutex_group);
lck_mtx_destroy(&np->f_clusterWriteLock, smbfs_mutex_group);
if (!vnode_isnamedstream(vp))
lck_mtx_destroy(&np->rfrkMetaLock, smbfs_mutex_group);
}
SMB_FREE(np->n_symlink_target, M_TEMP);
np->n_symlink_target_len = 0;
np->n_symlink_cache_timer = 0;
if (!vnode_isnamedstream(vp)) {
smbfs_clear_acl_cache(np);
lck_mtx_destroy(&np->f_ACLCacheLock, smbfs_mutex_group);
}
SMB_FREE(np->n_name, M_SMBNODENAME);
SMB_FREE(np->n_sname, M_SMBNODENAME);
vnode_clearfsnode(vp);
smbnode_unlock(np);
CLR(np->n_flag, (NALLOC|NTRANSIT));
if (ISSET(np->n_flag, NWALLOC) || ISSET(np->n_flag, NWTRANSIT)) {
CLR(np->n_flag, (NWALLOC|NWTRANSIT));
wakeup(np);
}
lck_rw_destroy(&np->n_rwlock, smbfs_rwlock_group);
lck_rw_destroy(&np->n_name_rwlock, smbfs_rwlock_group);
SMB_FREE(np, M_SMBNODE);
if (dvp && (vnode_get(dvp) == 0)) {
vnode_rele(dvp);
vnode_put(dvp);
}
return 0;
}
static int
smbfs_getattr(struct smb_share *share, vnode_t vp, struct vnode_attr *vap,
vfs_context_t context)
{
if (share->ss_attributes & FILE_PERSISTENT_ACLS &&
(VATTR_IS_ACTIVE(vap, va_acl) || VATTR_IS_ACTIVE(vap, va_guuid) ||
VATTR_IS_ACTIVE(vap, va_uuuid))) {
DBG_ASSERT(!vnode_isnamedstream(vp));
(void)smbfs_getsecurity(share, VTOSMB(vp), vap, context);
}
return smbfs_update_cache(share, vp, vap, context);
}
static int
smbfs_vnop_getattr(struct vnop_getattr_args *ap)
{
int32_t error = 0;
struct smb_share *share;
struct smbnode *np;
if ((error = smbnode_lock(VTOSMB(ap->a_vp), SMBFS_SHARED_LOCK))) {
return (error);
}
np = VTOSMB(ap->a_vp);
np->n_lastvop = smbfs_vnop_getattr;
share = smb_get_share_with_reference(VTOSMBFS(ap->a_vp));
if ((!vnode_isdir(ap->a_vp)) && (np->f_openState == kNeedReopen)) {
(void)smbfs_smb_reopen_file(share, np, ap->a_context);
}
error = smbfs_getattr(share, ap->a_vp, ap->a_vap, ap->a_context);
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
static struct timespec fat1980_time = {SECONDSTO1980, 0};
static int
smbfs_setattr(struct smb_share *share, vnode_t vp, struct vnode_attr *vap,
vfs_context_t context)
{
struct smbnode *np = VTOSMB(vp);
struct smbmount *smp = VTOSMBFS(vp);
struct timespec *crtime, *mtime, *atime;
u_quad_t tsize = 0;
int error = 0, cerror, modified = 0;
uint16_t fid = 0;
uint32_t rights;
Boolean useFatTimes = (share->ss_fstype == SMB_FS_FAT);
if ((vnode_isnamedstream(vp)) &&
(vap->va_active & ~VNODE_ATTR_BIT(va_data_size))) {
SMBDEBUG("Using stream node %s to set something besides the size?\n",
np->n_name);
error = ENOTSUP;
goto out;
}
if (share->ss_attributes & FILE_PERSISTENT_ACLS &&
(VATTR_IS_ACTIVE(vap, va_acl) || VATTR_IS_ACTIVE(vap, va_guuid) ||
VATTR_IS_ACTIVE(vap, va_uuuid))) {
error = smbfs_setsecurity(share, vp, vap, context);
if (error)
goto out;
if (VATTR_IS_ACTIVE(vap, va_acl))
VATTR_SET_SUPPORTED(vap, va_acl);
if (VATTR_IS_ACTIVE(vap, va_guuid))
VATTR_SET_SUPPORTED(vap, va_guuid);
if (VATTR_IS_ACTIVE(vap, va_uuuid))
VATTR_SET_SUPPORTED(vap, va_uuuid);
modified = 1;
}
if ((VATTR_IS_ACTIVE(vap, va_mode)) || (VATTR_IS_ACTIVE(vap, va_flags))) {
int supportUnixBSDFlags = ((UNIX_CAPS(share) & UNIX_SFILEINFO_UNIX_INFO2_CAP)) ? TRUE : FALSE;
int supportUnixInfo2 = ((UNIX_CAPS(share) & UNIX_QFILEINFO_UNIX_INFO2_CAP)) ? TRUE : FALSE;
int darwin = (SSTOVC(share)->vc_flags & SMBV_DARWIN) ? TRUE : FALSE;
int dosattr = np->n_dosattr;
uint32_t vaflags = 0;
uint32_t vaflags_mask = SMB_FLAGS_NO_CHANGE;
uint64_t vamode = SMB_MODE_NO_CHANGE;
if (VATTR_IS_ACTIVE(vap, va_flags)) {
if (vap->va_flags & ~(SF_ARCHIVED | SF_IMMUTABLE | UF_IMMUTABLE | UF_HIDDEN))
{
error = EINVAL;
goto out;
}
vaflags_mask = np->n_flags_mask & EXT_REQUIRED_BY_MAC;
if (vap->va_flags & SF_ARCHIVED) {
dosattr &= ~SMB_EFA_ARCHIVE;
vaflags |= EXT_DO_NOT_BACKUP;
} else {
dosattr |= SMB_EFA_ARCHIVE;
}
if (supportUnixBSDFlags || darwin || (! vnode_isdir(vp))) {
if (vap->va_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) {
dosattr |= SMB_EFA_RDONLY;
vaflags |= EXT_IMMUTABLE;
} else {
dosattr &= ~SMB_EFA_RDONLY;
}
}
if ((! supportUnixBSDFlags) && (vnode_isdir(vp)))
dosattr &= ~SMB_EFA_ARCHIVE;
if (vap->va_flags & UF_HIDDEN) {
dosattr |= SMB_EFA_HIDDEN;
vaflags |= EXT_HIDDEN;
} else {
dosattr &= ~SMB_EFA_HIDDEN;
}
}
if (VATTR_IS_ACTIVE(vap, va_mode)) {
if (supportUnixInfo2 && (!vnode_islnk(vp))) {
vamode = vap->va_mode & ACCESSPERMS;
} else if ((share->ss_attributes & FILE_PERSISTENT_ACLS) &&
(darwin || !UNIX_CAPS(share))) {
vamode = vap->va_mode & ACCESSPERMS;
}
}
if (dosattr == np->n_dosattr) {
vaflags_mask = 0;
}
if (vaflags_mask || (vamode != SMB_MODE_NO_CHANGE)) {
if (!supportUnixInfo2) {
if (vaflags_mask) {
error = smbfs_smb_setpattr(share, np, NULL, 0, dosattr, context);
}
if (vamode != SMB_MODE_NO_CHANGE) {
error = smbfs_set_ace_modes(share, np, vamode, context);
}
} else if (supportUnixBSDFlags) {
error = smbfs_set_unix_info2(share, np, NULL, NULL, NULL,
SMB_SIZE_NO_CHANGE, vamode, vaflags,
vaflags_mask, context);
} else {
if (vamode != SMB_MODE_NO_CHANGE) {
error = smbfs_set_unix_info2(share, np, NULL, NULL, NULL,
SMB_SIZE_NO_CHANGE, vamode,
SMB_FLAGS_NO_CHANGE, vaflags_mask,
context);
}
if (vaflags_mask) {
error = smbfs_smb_setpattr(share, np, NULL, 0, dosattr, context);
}
}
if (error)
goto out;
}
if (VATTR_IS_ACTIVE(vap, va_mode)) {
if (vamode != SMB_MODE_NO_CHANGE) {
np->n_mode = vamode;
VATTR_SET_SUPPORTED(vap, va_mode);
}
}
if (VATTR_IS_ACTIVE(vap, va_flags)) {
np->n_dosattr = dosattr;
VATTR_SET_SUPPORTED(vap, va_flags);
}
}
if (VATTR_IS_ACTIVE(vap, va_data_size) && (vnode_isreg(vp))) {
uint32_t trycnt = 0;
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC);
rights = SMB2_FILE_WRITE_DATA | SMB2_FILE_APPEND_DATA;
do {
error = smbfs_tmpopen(share, np, rights, &fid, context);
if (error) {
SMB_LOG_IO("%s seteof open failed %d\n", np->n_name, error);
break;
}
tsize = np->n_size;
if (tsize < vap->va_data_size) {
error = smbfs_0extend(share, fid, tsize, vap->va_data_size, 0, context);
}
if (!error) {
error = smbfs_smb_seteof(share, np, fid, vap->va_data_size, context);
}
if (error == EBADF) {
trycnt++;
SMB_LOG_IO("%s seteof failed because of reconnect, trying again.\n",
np->n_name);
}
if ((!error) && (share->ss_fstype == SMB_FS_FAT) &&
(!UNIX_SERVER(SSTOVC(share)))) {
error = smbfs_smb_flush(share, fid, context);
if (!error)
np->n_flag &= ~NNEEDS_FLUSH;
}
(void)smbfs_tmpclose(share, np, fid, context);
} while ((error == EBADF) && (trycnt < SMB_MAX_REOPEN_CNT));
SMB_LOG_IO("%s: Calling smbfs_setsize, old eof = %lld new eof = %lld\n",
np->n_name, tsize, vap->va_data_size);
if (error) {
smbfs_setsize(vp, (off_t)tsize);
goto out;
} else {
smbfs_setsize(vp, (off_t)vap->va_data_size);
}
VATTR_SET_SUPPORTED(vap, va_data_size);
if (vnode_isnamedstream(vp)) {
vnode_t parent_vp = smb_update_rsrc_and_getparent(vp, TRUE);
if (parent_vp)
vnode_put(parent_vp);
}
modified = 1;
}
crtime = VATTR_IS_ACTIVE(vap, va_create_time) ? &vap->va_create_time : NULL;
mtime = VATTR_IS_ACTIVE(vap, va_modify_time) ? &vap->va_modify_time : NULL;
atime = VATTR_IS_ACTIVE(vap, va_access_time) ? &vap->va_access_time : NULL;
if (crtime && (timespeccmp(crtime, &np->n_crtime, ==))) {
VATTR_SET_SUPPORTED(vap, va_create_time);
crtime = NULL;
}
if (mtime && (timespeccmp(mtime, &np->n_mtime, ==))) {
VATTR_SET_SUPPORTED(vap, va_modify_time);
mtime = NULL;
}
if (atime && (timespeccmp(atime, &np->n_atime, ==))) {
VATTR_SET_SUPPORTED(vap, va_access_time);
atime = NULL;
}
if (!crtime && mtime && (timespeccmp(mtime, &np->n_crtime, <))) {
crtime = mtime;
}
if (!crtime && !mtime && !atime) {
goto out;
}
retrySettingTime:
#ifdef SMB_DEBUG
if (crtime && mtime) {
SMBDEBUG("%s crtime = %ld:%ld mtime = %ld:%ld\n", np->n_name,
crtime->tv_sec, crtime->tv_nsec, mtime->tv_sec, mtime->tv_nsec);
} else if (crtime) {
SMBDEBUG("%s crtime = %ld:%ld\n", np->n_name, crtime->tv_sec, crtime->tv_nsec);
} else if (mtime) {
SMBDEBUG("%s mtime = %ld:%ld\n", np->n_name, mtime->tv_sec, mtime->tv_nsec);
}
#endif // SMB_DEBUG
if (useFatTimes) {
if (crtime && (timespeccmp(crtime, &fat1980_time, <))) {
crtime = &fat1980_time;
SMBDEBUG("%s FAT crtime.tv_sec = %ld crtime.tv_nsec = %ld\n",
np->n_name, crtime->tv_sec, crtime->tv_nsec);
}
if (mtime && (timespeccmp(mtime, &fat1980_time, <))) {
mtime = &fat1980_time;
SMBDEBUG("%s FAT mtime.tv_sec = %ld mtime.tv_nsec = %ld\n",
np->n_name, mtime->tv_sec, mtime->tv_nsec);
}
if (atime && (timespeccmp(atime, &fat1980_time, <))) {
atime = NULL;
}
}
rights = SMB2_FILE_WRITE_ATTRIBUTES;
if ((SSTOVC(share)->vc_flags & SMBV_NT4) ||
(smp->sm_flags & MNT_REQUIRES_FILEID_FOR_TIME) ||
((!vnode_isdir(vp)) && np->f_refcnt && (np->f_rights & rights))) {
error = smbfs_tmpopen(share, np, rights, &fid, context);
if (error)
goto out;
error = smbfs_smb_setfattrNT(share, np->n_dosattr, fid, crtime,
mtime, atime, context);
cerror = smbfs_tmpclose(share, np, fid, context);
if (cerror)
SMBERROR("error %d closing fid %d file %s\n", cerror,
fid, np->n_name);
} else {
error = smbfs_smb_setpattrNT(share, np, np->n_dosattr, crtime, mtime,
atime, context);
if (error == ENOTSUP) {
SMBWARNING("Server does not support setting time by path, fallback to old method\n");
smp->sm_flags |= MNT_REQUIRES_FILEID_FOR_TIME;
error = smbfs_tmpopen(share, np, rights, &fid, context);
if (!error) {
error = smbfs_smb_setfattrNT(share, np->n_dosattr, fid,
crtime, mtime, atime, context);
(void)smbfs_tmpclose(share, np, fid, context);
}
}
}
if (error && !useFatTimes &&
((crtime && (crtime->tv_sec < 0)) || (mtime && (mtime->tv_sec < 0)))) {
useFatTimes = TRUE;
goto retrySettingTime;
}
if (error)
goto out;
if (crtime) {
VATTR_SET_SUPPORTED(vap, va_create_time);
np->n_crtime = *crtime;
}
if (mtime) {
VATTR_SET_SUPPORTED(vap, va_modify_time);
np->n_mtime = *mtime;
}
if (atime) {
VATTR_SET_SUPPORTED(vap, va_access_time);
np->n_atime = *atime;
}
if (crtime || mtime || atime)
nanotime(&np->n_chtime);
out:
if (modified) {
np->attribute_cache_timer = 0;
}
return (error);
}
static int
smbfs_vnop_setattr(struct vnop_setattr_args *ap)
{
int32_t error = 0;
struct smbnode *np;
struct smb_share *share;
if ((error = smbnode_lock(VTOSMB(ap->a_vp), SMBFS_EXCLUSIVE_LOCK))) {
return (error);
}
np = VTOSMB(ap->a_vp);
np->n_lastvop = smbfs_vnop_setattr;
share = smb_get_share_with_reference(VTOSMBFS(ap->a_vp));
error = smbfs_setattr (share, ap->a_vp, ap->a_vap, ap->a_context);
smb_share_rele(share, ap->a_context);
smbnode_unlock(VTOSMB(ap->a_vp));
if (vnode_isnamedstream(ap->a_vp)) {
vnode_t parent_vp = vnode_getparent(ap->a_vp);
if (parent_vp) {
VTOSMB(parent_vp)->attribute_cache_timer = 0;
vnode_put(parent_vp);
}
}
return (error);
}
static int
smbfs_vnop_blockmap(struct vnop_blockmap_args *ap)
{
if (ap->a_run)
*ap->a_run = ap->a_size;
*ap->a_bpn = (daddr64_t)(ap->a_foffset / PAGE_SIZE);
if (ap->a_poff)
*(int32_t *)ap->a_poff = 0;
return (0);
}
static int
smbfs_vnop_strategy(struct vnop_strategy_args *ap)
{
struct buf *bp = ap->a_bp;
vnode_t vp = buf_vnode(bp);
int32_t bflags = buf_flags(bp);
struct smbnode *np = VTOSMB(vp);
caddr_t io_addr = 0;
uio_t uio = NULL;
int32_t error;
uint16_t fid;
struct smb_share *share;
uint32_t trycnt = 0;
if (np->f_openState == kNeedRevoke) {
SMBERROR("%s waiting to be revoked\n", np->n_name);
error = EIO;
buf_seterror(bp, error);
goto exit;
}
if ((error = buf_map(bp, &io_addr))) {
panic("smbfs_vnop_strategy: buf_map() failed with (%d)", error);
}
uio = uio_create(1, ((off_t)buf_blkno(bp)) * PAGE_SIZE, UIO_SYSSPACE,
(bflags & B_READ) ? UIO_READ : UIO_WRITE);
if (!uio) {
panic("smbfs_vnop_strategy: uio_create() failed");
}
uio_addiov(uio, CAST_USER_ADDR_T(io_addr), buf_count(bp));
if (FindFileRef(vp, buf_proc(bp), (bflags & B_READ) ? kAccessRead : kAccessWrite,
kCheckDenyOrLocks, uio_offset(uio), uio_resid(uio), NULL, &fid)) {
fid = np->f_fid;
}
DBG_ASSERT(fid);
SMB_LOG_IO("%s: %s offset %lld, size %lld, bflags 0x%x\n",
(bflags & B_READ) ? "Read":"Write", np->n_name, uio_offset(uio),
uio_resid(uio), bflags);
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (bflags & B_READ) {
error = smbfs_doread(share, (off_t)np->n_size, uio, fid, NULL);
} else {
error = smbfs_dowrite(share, (off_t)np->n_size, uio, fid, 0, NULL);
}
while ((error == EBADF) && (trycnt < SMB_MAX_REOPEN_CNT)) {
lck_mtx_lock(&np->f_openStateLock);
SMB_LOG_IO("%s failed the %s, because of reconnect, openState = 0x%x. Try again.\n",
np->n_name, (bflags & B_READ) ? "READ" : "WRITE", np->f_openState);
if (np->f_openState == kNeedRevoke) {
lck_mtx_unlock(&np->f_openStateLock);
break;
}
np->f_openState |= kNeedReopen;
lck_mtx_unlock(&np->f_openStateLock);
smb_share_rele(share, NULL);
share = smb_get_share_with_reference(VTOSMBFS(vp));
uio_free(uio);
uio = uio_create(1, ((off_t)buf_blkno(bp)) * PAGE_SIZE, UIO_SYSSPACE,
(bflags & B_READ) ? UIO_READ : UIO_WRITE);
if (!uio) {
break;
}
uio_addiov(uio, CAST_USER_ADDR_T(io_addr), buf_count(bp));
error = smbfs_smb_open(share, np, np->f_rights,
NTCREATEX_SHARE_ACCESS_ALL, &fid, NULL);
if (error) {
break;
}
if (bflags & B_READ) {
error = smbfs_doread(share, (off_t)np->n_size, uio, fid, NULL);
} else {
error = smbfs_dowrite(share, (off_t)np->n_size, uio, fid, 0, NULL);
}
(void)smbfs_smb_close(share, fid, NULL);
trycnt++;
}
if (bflags & B_READ) {
while ((error == 0) && (uio_resid(uio))) {
size_t bytes_to_zero = (uio_resid(uio) > PAGE_SIZE) ? PAGE_SIZE : (size_t)uio_resid(uio);
bzero((caddr_t) (io_addr + buf_count(bp) - uio_resid(uio)), bytes_to_zero);
uio_update(uio, bytes_to_zero);
}
} else {
lck_mtx_lock(&np->f_clusterWriteLock);
if ((u_quad_t)uio_offset(uio) >= np->n_size) {
nanouptime(&np->n_sizetime);
np->waitOnClusterWrite = FALSE;
SMB_LOG_IO("%s: TURNING OFF waitOnClusterWrite np->n_size = %lld\n",
np->n_name, np->n_size);
}
lck_mtx_unlock(&np->f_clusterWriteLock);
}
if (error) {
SMBERROR("%s on %s failed with an error of %d\n",
(bflags & B_READ) ? "READ" : "WRITE", np->n_name, error);
np->f_clusterCloseError = error;
if ( (error == ENOTCONN) || (error == EBADF) || (error == ETIMEDOUT) ) {
SMB_LOG_IO("failed with %d, returning ENXIO instead\n", error);
error = ENXIO;
}
}
smb_share_rele(share, NULL);
buf_seterror(bp, error);
buf_setresid(bp, (uint32_t)uio_resid(uio));
if ((error = buf_unmap(bp)))
panic("smbfs_vnop_strategy: buf_unmap() failed with (%d)", error);
exit:
if (error) {
SMB_LOG_IO("%s: buf_resid(bp) %d, error = %d \n",
np->n_name, buf_resid(bp), error);
}
if (uio != NULL)
uio_free(uio);
buf_biodone(bp);
return (error);
}
static int
smbfs_vnop_read(struct vnop_read_args *ap)
{
vnode_t vp = ap->a_vp;
uio_t uio = ap->a_uio;
int error = 0;
struct smbnode *np = NULL;
uint16_t fid = 0;
struct smb_share *share;
if (!vnode_isreg(vp) && !vnode_islnk(vp))
return (EPERM);
if (uio_resid(uio) == 0)
return (0);
if (uio_offset(uio) < 0)
return (EINVAL);
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_SHARED_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_read;
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (!np->f_refcnt) {
error = smbfs_open(share, vp, FREAD, ap->a_context);
if (error)
goto exit;
else np->f_needClose = 1;
} else {
error = smbfs_smb_reopen_file(share, np, ap->a_context);
if (error) {
SMBDEBUG(" %s waiting to be revoked\n", np->n_name);
goto exit;
}
}
if ( smbfsIsCacheable(vp) && !(ap->a_ioflag & IO_NOCACHE)) {
error = cluster_read(vp, uio, (off_t) np->n_size, ap->a_ioflag);
if (error) {
SMB_LOG_IO("%s failed cluster_read with an error of %d\n",
np->n_name, error);
}
if (error == EACCES) {
if (np->n_flag & NISMAPPED) {
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC);
} else {
cluster_push(vp, IO_SYNC);
}
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_INVALIDATE);
vnode_setnocache(vp);
error = 0;
} else {
goto exit;
}
}
if (error)
goto exit;
if (VTOSMB(vp)->n_flag & NISMAPPED) {
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC);
} else {
cluster_push(vp, IO_SYNC);
}
ubc_msync (vp, uio_offset(uio), uio_offset(uio)+ uio_resid(uio), NULL,
UBC_INVALIDATE);
if (FindFileRef(vp, vfs_context_proc(ap->a_context), kAccessRead,
kCheckDenyOrLocks, uio_offset(uio), uio_resid(uio),
NULL, &fid)) {
fid = np->f_fid;
}
DBG_ASSERT(fid);
error = smbfs_doread(share, (off_t)np->n_size, uio, fid, ap->a_context);
while (error == EBADF) {
SMB_LOG_IO("%s failed the non cache read, because of reconnect. Try again.\n",
np->n_name);
smb_share_rele(share, ap->a_context);
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (smbfs_io_reopen(share, vp, uio, kAccessRead, &fid, error, ap->a_context) == 0) {
error = smbfs_doread(share, (off_t)np->n_size, uio, fid,
ap->a_context);
} else {
SMB_LOG_IO("%s : The Read reopen failed.\n", np->n_name);
break;
}
}
if (error) {
SMB_LOG_IO("%s failed non cached read with an error of %d\n", np->n_name, error);
}
exit:
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_write(struct vnop_write_args *ap)
{
vnode_t vp = ap->a_vp;
vnode_t parent_vp = NULL;
struct smbnode *np = NULL;
struct smb_share *share;
uio_t uio = ap->a_uio;
int error = 0;
uint16_t fid = 0;
u_quad_t originalEOF;
user_size_t writeCount;
if ( !vnode_isreg(vp))
return (EPERM);
if (uio_offset(uio) < 0)
return (EINVAL);
if (uio_resid(uio) == 0)
return (0);
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_write;
share = smb_get_share_with_reference(VTOSMBFS(vp));
error = smbfs_smb_reopen_file(share, np, ap->a_context);
if (error) {
SMBDEBUG(" %s waiting to be revoked\n", np->n_name);
goto exit;
}
if (ap->a_ioflag & IO_APPEND)
uio_setoffset(uio, np->n_size);
originalEOF = np->n_size;
if (smbfsIsCacheable(vp) && !(ap->a_ioflag & IO_NOCACHE)) {
u_quad_t writelimit;
u_quad_t newEOF;
u_quad_t zero_head_off;
u_quad_t zero_tail_off;
int32_t lflag;
lflag = ap->a_ioflag & ~(IO_TAILZEROFILL | IO_HEADZEROFILL |
IO_NOZEROVALID | IO_NOZERODIRTY);
zero_head_off = 0;
zero_tail_off = 0;
writelimit = uio_offset(uio) + uio_resid(uio);
if ((uint64_t)writelimit > np->n_size) {
newEOF = writelimit;
if ((uint64_t) uio_offset(uio) > np->n_size) {
zero_head_off = np->n_size;
lflag |= IO_HEADZEROFILL;
}
zero_tail_off = (writelimit + (PAGE_SIZE_64 - 1)) & ~PAGE_MASK_64;
if (zero_tail_off > newEOF) {
zero_tail_off = newEOF;
}
if (zero_tail_off > writelimit) {
lflag |= IO_TAILZEROFILL;
}
} else
newEOF = np->n_size;
lck_mtx_lock(&np->f_clusterWriteLock);
if (originalEOF < newEOF) {
np->waitOnClusterWrite = TRUE;
np->n_size = newEOF;
SMB_LOG_IO("%s: TURNING ON waitOnClusterWrite old eof = %lld new eof = %lld\n",
np->n_name, originalEOF, newEOF);
}
lck_mtx_unlock(&np->f_clusterWriteLock);
error = cluster_write(vp, uio, originalEOF, newEOF, zero_head_off, zero_tail_off, lflag);
if (error) {
lck_mtx_lock(&np->f_clusterWriteLock);
np->n_size = originalEOF;
np->waitOnClusterWrite = FALSE;
lck_mtx_unlock(&np->f_clusterWriteLock);
SMB_LOG_IO("%s failed cluster_write with an error of %d\n", np->n_name, error);
}
if (error == EACCES) {
if (np->n_flag & NISMAPPED) {
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC);
} else {
cluster_push(vp, IO_SYNC);
}
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_INVALIDATE);
vnode_setnocache(vp);
error = 0;
} else {
goto exit;
}
}
if (error)
goto exit;
ubc_msync(vp, uio_offset(uio), uio_offset(uio)+ uio_resid(uio), NULL,
UBC_PUSHDIRTY | UBC_SYNC);
ubc_msync(vp, uio_offset(uio), uio_offset(uio)+ uio_resid(uio), NULL,
UBC_INVALIDATE);
if (FindFileRef(vp, vfs_context_proc(ap->a_context), kAccessWrite,
kCheckDenyOrLocks, uio_offset(uio), uio_resid(uio), NULL, &fid)) {
fid = np->f_fid;
}
DBG_ASSERT(fid);
writeCount = uio_resid(ap->a_uio);
do {
if (uio && (uio != ap->a_uio)) {
uio_free(uio);
}
uio = uio_duplicate(ap->a_uio);
if (uio == NULL) {
uio = ap->a_uio;
}
error = smbfs_dowrite(share, (off_t)np->n_size, uio, fid, ap->a_ioflag,
ap->a_context);
if (error == EBADF) {
smb_share_rele(share, ap->a_context);
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (smbfs_io_reopen(share, vp, uio, kAccessWrite, &fid, error,
ap->a_context) != 0) {
break;
}
}
} while ((error == EBADF) && (uio != ap->a_uio));
if (uio != ap->a_uio) {
writeCount -= uio_resid(uio);
uio_update( ap->a_uio, writeCount);
uio_free(uio);
uio = ap->a_uio;
}
if (!error && !(ap->a_ioflag & IO_SYNC)) {
VTOSMB(vp)->n_flag |= NNEEDS_FLUSH;
} else if (error) {
SMB_LOG_IO("%s failed non cached write with an error of %d\n",
np->n_name, error);
}
exit:
if (!error && ((uint64_t) uio_offset(uio) > originalEOF)) {
if (!UNIX_SERVER(SSTOVC(share))) {
if ((np->f_accessMode & kDenyMask) == (kDenyWrite | kDenyRead)) {
np->n_flag |= NNEEDS_EOF_SET;
} else {
uint32_t rights = SMB2_FILE_WRITE_DATA | SMB2_FILE_APPEND_DATA;
do {
error = smbfs_tmpopen(share, np, rights, &fid,
ap->a_context);
if (error) {
SMB_LOG_IO("%s reopen failed %d\n", np->n_name, error);
break;
}
error = smbfs_smb_seteof(share, np, fid, uio_offset(uio), ap->a_context);
if (share->ss_fstype == SMB_FS_FAT) {
error = smbfs_smb_flush(share, fid, ap->a_context);
if (!error)
np->n_flag &= ~NNEEDS_FLUSH;
}
(void)smbfs_tmpclose(share, np, fid, ap->a_context);
} while (error == EBADF);
}
}
smbfs_setsize(vp, uio_offset(uio));
SMB_LOG_IO("%s: Calling smbfs_setsize, old eof = %lld new eof = %lld time %ld:%ld\n",
np->n_name, originalEOF, uio_offset(uio),
np->n_sizetime.tv_sec, np->n_sizetime.tv_nsec);
np->n_flag |= NATTRCHANGED;
}
if (vnode_isnamedstream(vp))
parent_vp = smb_update_rsrc_and_getparent(vp, FALSE);
smb_share_rele(share, ap->a_context);
smbnode_unlock(VTOSMB(vp));
if (parent_vp) {
VTOSMB(parent_vp)->attribute_cache_timer = 0;
vnode_put(parent_vp);
}
return (error);
}
static void
smbfs_set_create_vap(struct smb_share *share, struct vnode_attr *vap, vnode_t vp,
vfs_context_t context, int set_mode_now)
{
struct smbnode *np = VTOSMB(vp);
struct smbmount *smp = np->n_mount;
struct vnode_attr svrva;
kauth_acl_t savedacl = NULL;
int error;
int unix_info2 = ((UNIX_CAPS(share) & UNIX_QFILEINFO_UNIX_INFO2_CAP)) ? TRUE : FALSE;
VATTR_INIT(&svrva);
VATTR_WANTED(&svrva, va_acl);
VATTR_WANTED(&svrva, va_uuuid);
VATTR_WANTED(&svrva, va_guuid);
if (VATTR_IS_ACTIVE(vap, va_flags) && (vap->va_flags == 0)) {
VATTR_SET_SUPPORTED(vap, va_flags);
VATTR_CLEAR_ACTIVE(vap, va_flags);
}
if (VATTR_IS_ACTIVE(vap, va_create_time)) {
VATTR_SET_SUPPORTED(vap, va_create_time);
VATTR_CLEAR_ACTIVE(vap, va_create_time);
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
VATTR_SET_SUPPORTED(vap, va_modify_time);
VATTR_CLEAR_ACTIVE(vap, va_modify_time);
}
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
VATTR_CLEAR_ACTIVE(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_access_time);
}
if (VATTR_IS_ACTIVE(vap, va_change_time)) {
VATTR_CLEAR_ACTIVE(vap, va_change_time);
VATTR_SET_SUPPORTED(vap, va_change_time);
}
if (VATTR_IS_ACTIVE(vap, va_mode)) {
if (set_mode_now == TRUE) {
VATTR_SET_SUPPORTED(vap, va_mode);
if ( !vnode_isreg(vp) || !unix_info2 ) {
VATTR_CLEAR_ACTIVE(vap, va_mode);
}
} else {
if (vnode_isreg(vp) && unix_info2 &&
((vap->va_mode & (S_IRUSR | S_IWUSR)) != (S_IRUSR | S_IWUSR))) {
np->create_va_mode = vap->va_mode;
np->set_create_va_mode = TRUE;
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_CLEAR_ACTIVE(vap, va_mode);
} else if (!unix_info2) {
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_CLEAR_ACTIVE(vap, va_mode);
}
}
}
if (VATTR_IS_ACTIVE(vap, va_uid)) {
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_CLEAR_ACTIVE(vap, va_uid);
}
if (VATTR_IS_ACTIVE(vap, va_gid)) {
VATTR_SET_SUPPORTED(vap, va_gid);
VATTR_CLEAR_ACTIVE(vap, va_gid);
if (vap->va_gid != smp->sm_args.gid)
SMB_LOG_ACCESS("They want to set the gid to %d from %d\n",
vap->va_gid, smp->sm_args.gid);
}
if (VATTR_IS_ACTIVE(vap, va_uuuid)) {
VATTR_SET_SUPPORTED(vap, va_uuuid);
if (is_memberd_tempuuid(&vap->va_uuuid))
VATTR_CLEAR_ACTIVE(vap, va_uuuid);
}
if (VATTR_IS_ACTIVE(vap, va_guuid)) {
VATTR_SET_SUPPORTED(vap, va_guuid);
if (is_memberd_tempuuid(&vap->va_guuid))
VATTR_CLEAR_ACTIVE(vap, va_guuid);
}
if (VATTR_IS_ACTIVE(vap, va_acl)) {
VATTR_SET_SUPPORTED(vap, va_acl);
if ((vap->va_acl == NULL) || (vap->va_acl->acl_entrycount == 0))
VATTR_CLEAR_ACTIVE(vap, va_acl);
}
if (!VATTR_IS_ACTIVE(vap, va_acl))
goto do_setattr;
error = smbfs_getattr(share, vp, &svrva, context);
if (error) {
SMBWARNING("Error %d returned while gettting the inherit ACLs from the server\n",
error);
goto out;
}
if (!VATTR_IS_SUPPORTED(&svrva, va_acl) || svrva.va_acl == NULL ||
svrva.va_acl->acl_entrycount == 0) {
goto do_setattr;
}
error = smbfs_compose_create_acl(vap, &svrva, &savedacl);
if (error)
goto out;
do_setattr:
error = smbfs_setattr(share, vp, vap, context);
if (error)
SMBERROR("smbfs_setattr, error %d\n", error);
if (savedacl) {
kauth_acl_free(vap->va_acl);
vap->va_acl = savedacl;
}
out:
if (VATTR_IS_SUPPORTED(&svrva, va_acl) && svrva.va_acl != NULL)
kauth_acl_free(svrva.va_acl);
return;
}
static int
smbfs_create(struct smb_share *share, struct vnop_create_args *ap, char *target,
size_t targetlen)
{
vnode_t dvp = ap->a_dvp;
struct vnode_attr *vap = ap->a_vap;
vnode_t *vpp = ap->a_vpp;
struct componentname *cnp = ap->a_cnp;
struct smbnode *dnp = VTOSMB(dvp);
struct smbmount *smp = VTOSMBFS(dvp);
vnode_t vp;
struct smbfattr fattr;
const char *name = cnp->cn_nameptr;
size_t nmlen = cnp->cn_namelen;
int error;
struct timespec ts;
int unix_symlink = ((UNIX_CAPS(share) & UNIX_SFILEINFO_UNIX_LINK_CAP)) ? TRUE : FALSE;
*vpp = NULL;
if (vap->va_type != VREG && vap->va_type != VLNK)
return (ENOTSUP);
if (vap->va_type == VLNK) {
if (smp->sm_flags & MNT_SUPPORTS_REPARSE_SYMLINKS) {
error = smbfs_smb_create_reparse_symlink(share, dnp, name, nmlen, target,
targetlen, &fattr , ap->a_context);
} else if (unix_symlink) {
error = smbfs_smb_create_unix_symlink(share, dnp, name, nmlen, target,
targetlen, &fattr , ap->a_context);
} else {
error = smbfs_smb_create_windows_symlink(share, dnp, name, nmlen, target,
targetlen, &fattr , ap->a_context);
}
} else {
error = smbfs_smb_create(share, dnp, name, nmlen, SMB2_FILE_WRITE_DATA,
NULL, FILE_CREATE, 0, &fattr, ap->a_context);
}
if (error) {
if (error == ENOENT) {
SMBDEBUG("Creating %s returned ENOENT, resetting to EACCES\n", name);
error = EACCES;
}
return (error);
}
smbfs_attr_touchdir(dnp, (share->ss_fstype == SMB_FS_FAT));
fattr.fa_vtype = vap->va_type;
error = smbfs_nget(share, vnode_mount(dvp), dvp, name, nmlen, &fattr, &vp,
cnp->cn_flags, ap->a_context);
if (error)
goto bad;
nanouptime(&ts);
VTOSMB(vp)->finfo_cache = ts.tv_sec;
VTOSMB(vp)->rfrk_cache_timer = ts.tv_sec;
smbfs_set_create_vap(share, vap, vp, ap->a_context, FALSE);
if (vap->va_type == VLNK) {
smbfs_update_symlink_cache(VTOSMB(vp), target, targetlen);
VTOSMB(vp)->n_size = targetlen;
if (!unix_symlink)
VTOSMB(vp)->n_flag |= NWINDOWSYMLNK;
}
*vpp = vp;
smbnode_unlock(VTOSMB(vp));
if (dnp->n_flag & NNEGNCENTRIES) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(dvp);
}
bad:
if (!error)
smp->sm_statfstime = 0;
return (error);
}
static int
smbfs_vnop_create(struct vnop_create_args *ap)
{
vnode_t dvp = ap->a_dvp;
int error;
struct smbnode *dnp;
struct smb_share *share;
if ((error = smbnode_lock(VTOSMB(dvp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
dnp = VTOSMB(dvp);
dnp->n_lastvop = smbfs_vnop_create;
share = smb_get_share_with_reference(VTOSMBFS(dvp));
error = smbfs_create(share, ap, NULL, 0);
smb_share_rele(share, ap->a_context);
smbnode_unlock(dnp);
return (error);
}
static int
smbfs_remove(struct smb_share *share, vnode_t dvp, vnode_t vp,
struct componentname *cnp, int flags, vfs_context_t context)
{
#pragma unused(cnp)
struct smbnode *dnp = VTOSMB(dvp);
proc_t p = vfs_context_proc(context);
struct smbnode *np = VTOSMB(vp);
struct smbmount *smp = VTOSMBFS(vp);
int error;
DBG_ASSERT((!vnode_isnamedstream(vp)))
cache_purge(vp);
if (flags & VNODE_REMOVE_NODELETEBUSY) {
if (vnode_isinuse(vp, 0)) {
return (EBUSY);
} else {
vnode_t svpp = smbfs_find_vgetstrm(smp, np, SFM_RESOURCEFORK_NAME,
share->ss_maxfilenamelen);
if (svpp) {
if (vnode_isinuse(svpp, 0)) {
smbnode_unlock(VTOSMB(svpp));
vnode_put(svpp);
SMBDEBUG("%s: Cannot delete %s, resource fork in use\n", __FUNCTION__, vnode_getname(vp));
return (EBUSY);
} else {
smbnode_unlock(VTOSMB(svpp));
vnode_put(svpp);
}
}
}
}
if ((np->f_refcnt == 1) && np->f_needClose) {
error = smbfs_close(share, vp, FREAD, context);
if (error)
SMBWARNING("error %d closing %s\n", error, np->n_name);
}
if (np->f_refcnt) {
if ( (VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU)) {
error = smbfs_delete_openfile(share, dnp, np, context);
} else {
error = EBUSY;
}
if (! error)
return(0);
goto out;
}
error = smbfs_smb_delete(share, np, NULL, 0, 0, context);
if (error)
goto out;
smb_vhashrem(np);
smbfs_attr_touchdir(dnp, (share->ss_fstype == SMB_FS_FAT));
if (dnp->n_flag & NNEGNCENTRIES) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(dvp);
}
out:
if (error == EBUSY) {
char errbuf[32];
(void)proc_name(proc_pid(p), &errbuf[0], 32);
SMBWARNING("warning: pid %d(%.*s) unlink open file(%s)\n", proc_pid(p),
32, &errbuf[0], np->n_name);
}
if (!error) {
(void) vnode_recycle(vp);
smp->sm_statfstime = 0;
}
return (error);
}
static int
smbfs_vnop_remove(struct vnop_remove_args *ap)
{
vnode_t dvp = ap->a_dvp;
vnode_t vp = ap->a_vp;
int32_t error;
struct smb_share *share;
if (dvp == vp)
return (EINVAL);
if ((error = smbnode_lockpair(VTOSMB(dvp), VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
VTOSMB(dvp)->n_lastvop = smbfs_vnop_remove;
VTOSMB(vp)->n_lastvop = smbfs_vnop_remove;
share = smb_get_share_with_reference(VTOSMBFS(dvp));
error = smbfs_remove(share, dvp, vp, ap->a_cnp, ap->a_flags, ap->a_context);
smb_share_rele(share, ap->a_context);
smbnode_unlockpair(VTOSMB(dvp), VTOSMB(vp));
return (error);
}
static int
smbfs_rmdir(struct smb_share *share, vnode_t dvp, vnode_t vp,
struct componentname *cnp, vfs_context_t context)
{
#pragma unused(cnp)
struct smbmount *smp = VTOSMBFS(vp);
struct smbnode *dnp = VTOSMB(dvp);
struct smbnode *np = VTOSMB(vp);
int error;
uint16_t fid;
if (dvp == vp) {
error = EINVAL;
goto bad;
}
cache_purge(vp);
error = smbfs_smb_rmdir(share, np, context);
if (error)
goto bad;
smbfs_attr_touchdir(dnp, (share->ss_fstype == SMB_FS_FAT));
smb_vhashrem(np);
np->d_needReopen = FALSE;
fid = np->d_fid;
np->d_fid = 0;
if (fid) {
(void)smbfs_tmpclose(share, np, fid, context);
}
if (dnp->n_flag & NNEGNCENTRIES) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(dvp);
}
bad:
if (!error) {
smp->sm_statfstime = 0;
(void) vnode_recycle(vp);
}
return (error);
}
static int smbfs_vnop_rmdir(struct vnop_rmdir_args *ap)
{
vnode_t dvp = ap->a_dvp;
vnode_t vp = ap->a_vp;
int32_t error;
struct smb_share *share;
if (!vnode_isdir(vp))
return (ENOTDIR);
if (dvp == vp)
return (EINVAL);
if ((error = smbnode_lockpair(VTOSMB(dvp), VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
VTOSMB(dvp)->n_lastvop = smbfs_vnop_rmdir;
VTOSMB(vp)->n_lastvop = smbfs_vnop_rmdir;
share = smb_get_share_with_reference(VTOSMBFS(dvp));
error = smbfs_rmdir(share, dvp, vp, ap->a_cnp, ap->a_context);
smb_share_rele(share, ap->a_context);
smbnode_unlockpair(VTOSMB(dvp), VTOSMB(vp));
return (error);
}
static int
smbfs_vnop_rename(struct vnop_rename_args *ap)
{
vnode_t fvp = ap->a_fvp;
vnode_t tvp = ap->a_tvp;
vnode_t fdvp = ap->a_fdvp;
vnode_t tdvp = ap->a_tdvp;
struct smbmount *smp = VFSTOSMBFS(vnode_mount(fvp));
struct smb_share *share = NULL;
struct componentname *tcnp = ap->a_tcnp;
struct componentname *fcnp = ap->a_fcnp;
proc_t p = vfs_context_proc(ap->a_context);
int error = 0;
struct smbnode *fnp = NULL;
struct smbnode *tdnp = NULL;
struct smbnode *fdnp = NULL;
int vtype;
struct smbnode * lock_order[4] = {NULL};
int lock_cnt = 0;
int ii;
if ((vnode_mount(fvp) != vnode_mount(tdvp)) ||
(tvp && (vnode_mount(fvp) != vnode_mount(tvp))))
return (EXDEV);
vtype = vnode_vtype(fvp);
if ( (vtype != VDIR) && (vtype != VREG) && (vtype != VLNK) )
return (EINVAL);
lck_mtx_lock(&smp->sm_reclaim_renamelock);
if (fdvp == tdvp)
lock_order[lock_cnt++] = VTOSMB(fdvp);
else if (VTOSMB(fdvp)->n_parent && (VTOSMB(fdvp)->n_parent == VTOSMB(tdvp))) {
lock_order[lock_cnt++] = VTOSMB(tdvp);
lock_order[lock_cnt++] = VTOSMB(fdvp);
} else if (VTOSMB(tdvp)->n_parent && (VTOSMB(tdvp)->n_parent == VTOSMB(fdvp))) {
lock_order[lock_cnt++] = VTOSMB(fdvp);
lock_order[lock_cnt++] = VTOSMB(tdvp);
} else if (VTOSMB(fdvp) < VTOSMB(tdvp)) {
lock_order[lock_cnt++] = VTOSMB(fdvp);
lock_order[lock_cnt++] = VTOSMB(tdvp);
} else {
lock_order[lock_cnt++] = VTOSMB(tdvp);
lock_order[lock_cnt++] = VTOSMB(fdvp);
}
if ((tvp == NULL) || (tvp == fvp) || (tvp == fdvp))
lock_order[lock_cnt++] = VTOSMB(fvp);
else {
if (VTOSMB(fvp) < VTOSMB(tvp)) {
lock_order[lock_cnt++] = VTOSMB(fvp);
lock_order[lock_cnt++] = VTOSMB(tvp);
} else {
lock_order[lock_cnt++] = VTOSMB(tvp);
lock_order[lock_cnt++] = VTOSMB(fvp);
}
}
DBG_LOCKLIST_ASSERT(lock_cnt, lock_order);
lck_mtx_unlock(&smp->sm_reclaim_renamelock);
for (ii=0; ii<lock_cnt; ii++) {
if (error)
lock_order[ii] = NULL;
else if ((error = smbnode_lock(lock_order[ii], SMBFS_EXCLUSIVE_LOCK)))
lock_order[ii] = NULL;
}
if (error)
goto out;
fdnp = VTOSMB(fdvp);
fnp = VTOSMB(fvp);
tdnp = VTOSMB(tdvp);
fdnp->n_lastvop = smbfs_vnop_rename;
fnp->n_lastvop = smbfs_vnop_rename;
tdnp->n_lastvop = smbfs_vnop_rename;
if (tvp != NULL)
VTOSMB(tvp)->n_lastvop = smbfs_vnop_rename;
share = smb_get_share_with_reference(smp);
if (node_isimmutable(share, fvp)) {
SMBWARNING( "%s is a locked file : Permissions error on delete\n",
fnp->n_name);
error = EPERM;
goto out;
}
if (fvp == tvp)
tvp = NULL;
if (tvp && (fdvp == tdvp) && (fnp->n_nmlen == VTOSMB(tvp)->n_nmlen) &&
(strncasecmp((char *)fnp->n_name, (char *)VTOSMB(tvp)->n_name,
fnp->n_nmlen) == 0)) {
SMBWARNING("Not removing target, same file. %s ==> %s\n",
fnp->n_name, VTOSMB(tvp)->n_name);
smb_vhashrem(VTOSMB(tvp));
(void) vnode_recycle(tvp);
tvp = NULL;
}
if ((tvp) && (!(share->ss_attributes & FILE_NAMED_STREAMS)) &&
(fnp->n_nmlen > 2) && (fnp->n_name[0] == '.') && (fnp->n_name[1] == '_')) {
const char *name = (const char *)fnp->n_name;
size_t nmlen = fnp->n_nmlen;
struct smbfattr fattr;
error = smbfs_lookup(share, fdnp, &name, &nmlen, &fattr, ap->a_context);
if (error == ENOENT)
tvp = NULL;
if (name != (char *)fnp->n_name) {
SMB_FREE(name, M_SMBNODENAME);
}
}
if (tvp) {
if (vnode_isdir(tvp)) {
if (share->ss_attributes & FILE_PERSISTENT_ACLS)
error = smbfs_rmdir(share, tdvp, tvp, tcnp, ap->a_context);
else if ((vfs_context_suser(ap->a_context) == 0) ||
(smb_check_posix_access(ap->a_context, VTOSMB(tvp), S_IWOTH)))
error = smbfs_rmdir(share, tdvp, tvp, tcnp, ap->a_context);
else
error = EPERM;
} else {
error = smbfs_remove(share, tdvp, tvp, tcnp, 0, ap->a_context);
}
if (error)
goto out;
}
cache_purge(fvp);
if ((!vnode_isdir(fvp)) && (!vnode_isinuse(fvp, 0)) &&
(fnp->f_refcnt == 1) && fnp->f_needClose) {
error = smbfs_close(share, fvp, FREAD, ap->a_context);
if (error)
SMBWARNING("error %d closing %s\n", error, fnp->n_name);
}
if (vnode_isdir(fvp) && (fdvp != tdvp) && fnp->d_fid) {
(void)smbfs_tmpclose(share, fnp, fnp->d_fid, ap->a_context);
fnp->d_needReopen = TRUE;
fnp->d_fid = 0;
}
error = smbfs_smb_rename(share, fnp, tdnp, tcnp->cn_nameptr,
tcnp->cn_namelen, ap->a_context);
if ( (fdvp == tdvp) && error &&
(VC_CAPS(SSTOVC(share)) & SMB_CAP_INFOLEVEL_PASSTHRU))
error = smbfs_smb_t2rename(share, fnp, tcnp->cn_nameptr,
tcnp->cn_namelen, 1, NULL, ap->a_context);
if (!error) {
char *new_name;
uint32_t hashval;
uint32_t orig_flag = fnp->n_flag;
smb_vhashrem(fnp);
if (tdvp && (fdvp != tdvp)) {
if (!vnode_isvroot(tdvp)) {
if (vnode_get(tdvp) == 0) {
if (vnode_ref(tdvp) == 0) {
fnp->n_flag |= NREFPARENT;
OSIncrementAtomic(&tdnp->n_child_refcnt);
}
else {
fnp->n_flag &= ~NREFPARENT;
}
vnode_put(tdvp);
}
}
else
fnp->n_flag &= ~NREFPARENT;
if ((!vnode_isvroot(fdvp)) && (orig_flag & NREFPARENT)) {
if (vnode_get(fdvp) == 0) {
vnode_rele(fdvp);
vnode_put(fdvp);
OSDecrementAtomic(&fdnp->n_child_refcnt);
}
}
fnp->n_parent = VTOSMB(tdvp);
}
new_name = smb_strndup(tcnp->cn_nameptr, tcnp->cn_namelen);
if (new_name) {
char * old_name = fnp->n_name;
lck_rw_lock_exclusive(&fnp->n_name_rwlock);
fnp->n_name = new_name;
fnp->n_nmlen = tcnp->cn_namelen;
lck_rw_unlock_exclusive(&fnp->n_name_rwlock);
hashval = smbfs_hash(fnp->n_name, fnp->n_nmlen);
smb_vhashadd(fnp, hashval);
SMB_FREE(old_name, M_SMBNODENAME);
}
fnp->n_ino = smbfs_getino(fnp->n_parent, fnp->n_name, fnp->n_nmlen);
}
if (!error) {
int hiderr;
Boolean hideItem = (tcnp->cn_nameptr[0] == '.');
Boolean unHideItem = ((tcnp->cn_nameptr[0] != '.') &&
(fcnp->cn_nameptr[0] == '.'));
if (hideItem || unHideItem) {
hiderr = smbfs_set_hidden_bit(share, tdnp, tcnp->cn_nameptr,
tcnp->cn_namelen, hideItem,
ap->a_context);
if (hiderr) {
SMBWARNING("Error %d %s %s\n", hiderr, (hideItem) ? "hiding" :
"unhiding", tcnp->cn_nameptr);
}
}
}
if (error == EBUSY) {
char errbuf[32];
proc_name(proc_pid(p), &errbuf[0], 32);
SMBERROR("warning: pid %d(%.*s) rename open file(%s)\n", proc_pid(p),
32, &errbuf[0], fnp->n_name);
}
smbfs_attr_touchdir(fdnp, (share->ss_fstype == SMB_FS_FAT));
if (tdvp != fdvp)
smbfs_attr_touchdir(tdnp, (share->ss_fstype == SMB_FS_FAT));
if (!error) {
smp->sm_statfstime = 0;
if (tdnp->n_flag & NNEGNCENTRIES) {
tdnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(tdvp);
}
}
out:
if (share) {
smb_share_rele(share, ap->a_context);
}
if (error == EBUSY) {
char errbuf[32];
proc_name(proc_pid(p), &errbuf[0], 32);
SMBWARNING("warning: pid %d(%.*s) rename open file(%s)\n", proc_pid(p), 32, &errbuf[0], fnp->n_name);
}
for (ii=0; ii<lock_cnt; ii++)
if (lock_order[ii])
smbnode_unlock(lock_order[ii]);
return (error);
}
static int
smbfs_vnop_link(struct vnop_link_args *ap)
{
proc_t p = vfs_context_proc(ap->a_context);
struct smbnode *np = VTOSMB(ap->a_vp);
char errbuf[32];
proc_name(proc_pid(p), &errbuf[0], 32);
SMBERROR("warning: pid %d(%.*s) hardlink(%s)\n", proc_pid(p), 32, &errbuf[0], np->n_name);
return (err_link(ap));
}
static int smbfs_vnop_symlink(struct vnop_symlink_args *ap)
{
int error;
struct vnop_create_args a;
vnode_t dvp = ap->a_dvp;
struct smbnode *dnp;
struct smb_share *share;
if ((error = smbnode_lock(VTOSMB(dvp), SMBFS_EXCLUSIVE_LOCK)))
return error;
dnp = VTOSMB(dvp);
dnp->n_lastvop = smbfs_vnop_symlink;
a.a_dvp = dvp;
a.a_vpp = ap->a_vpp;
a.a_cnp = ap->a_cnp;
a.a_vap = ap->a_vap;
a.a_context = ap->a_context;
share = smb_get_share_with_reference(VTOSMBFS(dvp));
error = smbfs_create(share, &a, ap->a_target, strnlen(ap->a_target, PATH_MAX+1));
smb_share_rele(share, ap->a_context);
smbnode_unlock(dnp);
return (error);
}
static int
smbfs_vnop_readlink(struct vnop_readlink_args *ap)
{
vnode_t vp = ap->a_vp;
struct smbnode *np = NULL;
int error;
struct smb_share *share;
time_t symlinktimeo;
struct timespec ts;
if (vnode_vtype(vp) != VLNK)
return (EINVAL);
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_readlink;
share = smb_get_share_with_reference(VTOSMBFS(vp));
SMB_CACHE_TIME(ts, np, symlinktimeo);
if ((np->n_symlink_target) && ((share->ss_flags & SMBS_RECONNECTING) ||
(symlinktimeo > (ts.tv_sec - np->n_symlink_cache_timer)))) {
error = uiomove(np->n_symlink_target, (int)np->n_symlink_target_len, ap->a_uio);
} else if ((np->n_dosattr & SMB_EFA_REPARSE_POINT) &&
(np->n_reparse_tag == IO_REPARSE_TAG_SYMLINK)) {
error = smbfs_smb_reparse_read_symlink(share, np, ap->a_uio, ap->a_context);
} else if (np->n_flag & NWINDOWSYMLNK) {
error = smbfs_smb_windows_read_symlink(share, np, ap->a_uio, ap->a_context);
} else {
error = smbfs_smb_unix_read_symlink(share, np, ap->a_uio, ap->a_context);
}
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_mknod(struct vnop_mknod_args *ap)
{
proc_t p = vfs_context_proc(ap->a_context);
char errbuf[32];
proc_name(proc_pid(p), &errbuf[0], 32);
SMBERROR("warning: pid %d(%.*s) mknod(%s)\n", proc_pid(p), 32 , &errbuf[0],
ap->a_cnp->cn_nameptr);
return (err_mknod(ap));
}
static int
smbfs_vnop_mkdir(struct vnop_mkdir_args *ap)
{
vnode_t dvp = ap->a_dvp;
struct vnode_attr *vap = ap->a_vap;
vnode_t vp;
struct componentname *cnp = ap->a_cnp;
struct smbnode *dnp = NULL;
struct smbmount *smp = NULL;
struct smbfattr fattr;
const char *name = cnp->cn_nameptr;
size_t len = cnp->cn_namelen;
int error;
struct smb_share *share;
struct timespec ts;
if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
return (EEXIST);
if ((error = smbnode_lock(VTOSMB(dvp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
dnp = VTOSMB(dvp);
dnp->n_lastvop = smbfs_vnop_mkdir;
smp = dnp->n_mount;
share = smb_get_share_with_reference(smp);
error = smbfs_smb_mkdir(share, dnp, name, len, &fattr, ap->a_context);
if (error)
goto exit;
smbfs_attr_touchdir(dnp, (share->ss_fstype == SMB_FS_FAT));
error = smbfs_nget(share, vnode_mount(dvp), dvp, name, len, &fattr, &vp,
cnp->cn_flags, ap->a_context);
if (error)
goto bad;
nanouptime(&ts);
VTOSMB(vp)->finfo_cache = ts.tv_sec;
smbfs_set_create_vap(share, vap, vp, ap->a_context, FALSE);
*ap->a_vpp = vp;
smbnode_unlock(VTOSMB(vp));
error = 0;
if (dnp->n_flag & NNEGNCENTRIES) {
dnp->n_flag &= ~NNEGNCENTRIES;
cache_purge_negatives(dvp);
}
bad:
if (name != cnp->cn_nameptr) {
SMB_FREE(name, M_SMBNODENAME);
}
smp->sm_statfstime = 0;
exit:
smb_share_rele(share, ap->a_context);
smbnode_unlock(dnp);
return (error);
}
static int
smbfs_vnop_readdir(struct vnop_readdir_args *ap)
{
vnode_t vp = ap->a_vp;
uio_t uio = ap->a_uio;
int error;
int32_t numdirent = 0;
if (uio_offset(uio) < 0)
return (EINVAL);
if (!vnode_isdir(vp))
return (EPERM);
if (ap->a_eofflag)
*ap->a_eofflag = 0;
if (uio_resid(uio) == 0)
return (0);
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
VTOSMB(vp)->n_lastvop = smbfs_vnop_readdir;
error = smbfs_readvdir(vp, uio, ap->a_context, ap->a_flags, &numdirent);
if (error == ENOENT) {
if (ap->a_eofflag)
*ap->a_eofflag = 1;
error = 0;
}
if (ap->a_numdirent)
*ap->a_numdirent = numdirent;
smbnode_unlock(VTOSMB(vp));
return (error);
}
int32_t
smbfs_fsync(struct smb_share *share, vnode_t vp, int waitfor, int ubc_flags,
vfs_context_t context)
{
#pragma unused(waitfor, ubc_flags)
int error;
off_t size;
if (!vnode_isreg(vp)) {
return 0;
}
size = smb_ubc_getsize(vp);
if ((size > 0) && smbfsIsCacheable(vp)) {
if (VTOSMB(vp)->n_flag & NISMAPPED) {
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC);
} else {
cluster_push(vp, IO_SYNC);
}
}
error = smbfs_smb_fsync(share, VTOSMB(vp), context);
if (!error)
VTOSMBFS(vp)->sm_statfstime = 0;
return (error);
}
static int32_t
smbfs_vnop_fsync(struct vnop_fsync_args *ap)
{
int32_t error;
struct smb_share *share;
error = smbnode_lock(VTOSMB(ap->a_vp), SMBFS_EXCLUSIVE_LOCK);
if (error)
return (0);
VTOSMB(ap->a_vp)->n_lastvop = smbfs_vnop_fsync;
share = smb_get_share_with_reference(VTOSMBFS(ap->a_vp));
error = smbfs_fsync(share, ap->a_vp, ap->a_waitfor, 0, ap->a_context);
smb_share_rele(share, ap->a_context);
smbnode_unlock(VTOSMB(ap->a_vp));
return (error);
}
static int smbfs_vnop_pathconf(struct vnop_pathconf_args *ap)
{
struct smb_share *share;
int32_t *retval = ap->a_retval;
int error = 0;
share = smb_get_share_with_reference(VTOSMBFS(ap->a_vp));
switch (ap->a_name) {
case _PC_LINK_MAX:
*retval = 0;
break;
case _PC_NAME_MAX:
*retval = share->ss_maxfilenamelen;
break;
case _PC_PATH_MAX:
*retval = PATH_MAX;
break;
case _PC_CHOWN_RESTRICTED:
*retval = 1;
break;
case _PC_NO_TRUNC:
*retval = 0;
break;
case _PC_NAME_CHARS_MAX:
*retval = share->ss_maxfilenamelen;
break;
case _PC_CASE_SENSITIVE:
*retval = 0;
break;
case _PC_CASE_PRESERVING:
if (share->ss_attributes & FILE_CASE_PRESERVED_NAMES)
*retval = 1;
else *retval = 0;
break;
case _PC_FILESIZEBITS:
if (VC_CAPS(SSTOVC(share)) & SMB_CAP_LARGE_FILES)
*retval = 64;
else *retval = 32;
break;
case _PC_XATTR_SIZE_BITS:
if (!(share->ss_attributes & FILE_NAMED_STREAMS)) {
error = EINVAL;
} else if (VC_CAPS(SSTOVC(share)) & SMB_CAP_LARGE_FILES) {
*retval = 64;
} else {
*retval = 32;
}
break;
default:
error = EINVAL;
}
smb_share_rele(share, ap->a_context);
return (error);
}
static int32_t smbfs_vnop_ioctl(struct vnop_ioctl_args *ap)
{
vnode_t vp = ap->a_vp;
struct smbnode *np;
int32_t error = 0;
proc_t p = vfs_context_proc(ap->a_context);
struct smb_share *share;
struct smbmount *smp;
error = smbnode_lock(VTOSMB(ap->a_vp), SMBFS_EXCLUSIVE_LOCK);
if (error)
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_ioctl;
smp = VTOSMBFS(vp);
share = smb_get_share_with_reference(VTOSMBFS(vp));
switch (ap->a_command) {
case smbfsGetVCSockaddrFSCTL:
case smbfsGetVCSockaddrFSCTL_BASECMD: {
if (SSTOVC(share)->vc_saddr) {
memcpy(ap->a_data, SSTOVC(share)->vc_saddr,
SSTOVC(share)->vc_saddr->sa_len);
} else {
error = EINVAL;
}
break;
}
case smbfsUniqueShareIDFSCTL:
case smbfsUniqueShareIDFSCTL_BASECMD: {
struct UniqueSMBShareID *uniqueptr = (struct UniqueSMBShareID *)ap->a_data;
uniqueptr->error = 0;
if ((uniqueptr->flags & SMBFS_GET_ACCESS_INFO) ||
((uniqueptr->unique_id_len == smp->sm_args.unique_id_len) &&
(bcmp(smp->sm_args.unique_id, uniqueptr->unique_id,
uniqueptr->unique_id_len) == 0))) {
uniqueptr->user[0] = 0;
if (SSTOVC(share)->vc_flags & SMBV_GUEST_ACCESS) {
uniqueptr->connection_type = kConnectedByGuest;
strlcpy(uniqueptr->user, kGuestAccountName, SMB_MAXUSERNAMELEN + 1);
}
else if (SSTOVC(share)->vc_username) {
uniqueptr->connection_type = kConnectedByUser;
strlcpy(uniqueptr->user, SSTOVC(share)->vc_username, SMB_MAXUSERNAMELEN + 1);
} else {
uniqueptr->connection_type = kConnectedByKerberos;
}
uniqueptr->error = EEXIST;
}
}
break;
case smbfsByteRangeLock2FSCTL:
case smbfsByteRangeLock2FSCTL_BASECMD:
case smbfsByteRangeLockFSCTL:
case smbfsByteRangeLockFSCTL_BASECMD: {
struct ByteRangeLockPB *pb = (struct ByteRangeLockPB *) ap->a_data;
struct ByteRangeLockPB2 *pb2 = (struct ByteRangeLockPB2 *) ap->a_data;
uint32_t lck_pid;
uint32_t timo;
uint16_t fid = 0;
struct fileRefEntry *fndEntry = NULL;
uint16_t accessMode = 0;
int8_t flags;
if (vnode_isdir(vp)) {
error = EISDIR;
goto exit;
}
error = smbfs_smb_reopen_file(share, np, ap->a_context);
if (error) {
SMBDEBUG(" %s waiting to be revoked\n", np->n_name);
goto exit;
}
if (smbfsIsCacheable(vp)) {
if (np->n_flag & NISMAPPED) {
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_PUSHDIRTY | UBC_SYNC);
} else {
cluster_push(vp, IO_SYNC);
}
ubc_msync (vp, 0, ubc_getsize(vp), NULL, UBC_INVALIDATE);
vnode_setnocache(vp);
}
accessMode = np->f_accessMode;
if ( (ap->a_command == smbfsByteRangeLock2FSCTL) ||
(ap->a_command == smbfsByteRangeLock2FSCTL_BASECMD) ) {
int32_t openMode = 0;
if ( (error = file_flags(pb2->fd, &openMode)) ) {
goto exit;
}
if (openMode & FREAD) {
accessMode |= kAccessRead;
}
if (openMode & FWRITE) {
accessMode |= kAccessWrite;
}
if (openMode & FHASLOCK) {
accessMode |= kDenyWrite;
error = FindFileRef(vp, p, accessMode, kExactMatch, 0, 0,
&fndEntry, &fid);
if (error != 0) {
accessMode |= kDenyRead;
error = FindFileRef(vp, p, accessMode, kExactMatch, 0, 0,
&fndEntry, &fid);
}
if (error != 0) {
error = EBADF;
goto exit;
}
}
else {
error = FindFileRef(vp, p, accessMode, kExactMatch, 0, 0,
&fndEntry, &fid);
}
} else {
error = FindFileRef(vp, p, kAccessRead | kAccessWrite, kAnyMatch,
0, 0, &fndEntry, &fid);
}
if (error) {
uint32_t rights = 0;
uint32_t shareMode = NTCREATEX_SHARE_ACCESS_ALL;
proc_t
p = vfs_context_proc(ap->a_context);
if (np->f_refcnt <= 0) {
error = EBADF;
goto exit;
}
if (pb->unLockFlag == 1) {
error = EINVAL;
goto exit;
}
if (accessMode & kAccessRead)
rights |= SMB2_FILE_READ_DATA;
if (accessMode & kAccessWrite)
rights |= SMB2_FILE_APPEND_DATA | SMB2_FILE_WRITE_DATA;
if (accessMode & kDenyWrite)
shareMode &= ~NTCREATEX_SHARE_ACCESS_WRITE;
if (accessMode & kDenyRead)
shareMode &= ~NTCREATEX_SHARE_ACCESS_READ;
error = smbfs_smb_open(share, np, rights, shareMode, &fid,
ap->a_context);
if (error != 0)
goto exit;
AddFileRef(vp, p, accessMode, rights, fid, &fndEntry);
}
if (pb->startEndFlag) {
uint64_t fileSize = np->n_size;
pb->offset += fileSize;
pb->startEndFlag = 0;
}
flags = 0;
if (pb->unLockFlag)
flags |= SMB_LOCK_RELEASE;
else flags |= SMB_LOCK_EXCL;
lck_pid = 1;
timo = 0;
error = smbfs_smb_lock(share, flags, fid, lck_pid, pb->offset,
pb->length, timo, ap->a_context);
if (error == 0) {
AddRemoveByteRangeLockEntry (fndEntry, pb->offset, pb->length,
pb->unLockFlag, lck_pid);
pb->retRangeStart = pb->offset;
} else if ((!pb->unLockFlag) && (error == EACCES)) {
if (FindByteRangeLockEntry(fndEntry, pb->offset, pb->length, lck_pid))
error = EAGAIN;
}
}
break;
default:
error = ENOTSUP;
goto exit;
}
exit:
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int32_t
smbfs_vnop_advlock(struct vnop_advlock_args *ap)
{
int flags = ap->a_flags;
vnode_t vp = ap->a_vp;
struct smb_share *share;
struct smbnode *np;
int error = 0;
uint32_t timo;
off_t start = 0;
uint64_t len = -1;
uint32_t lck_pid;
if ( !vnode_isreg(vp))
return (EISDIR);
share = smb_get_share_with_reference(VTOSMBFS(vp));
if ((flags & F_POSIX) && ((UNIX_CAPS(share) & CIFS_UNIX_FCNTL_LOCKS_CAP) == 0)) {
smb_share_rele(share, ap->a_context);
return(err_advlock(ap));
}
if ((flags & (F_FLOCK | F_POSIX)) == 0) {
smb_share_rele(share, ap->a_context);
SMBWARNING("Lock flag we do not understand %x\n", flags);
return(err_advlock(ap));
}
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK))) {
smb_share_rele(share, ap->a_context);
return (error);
}
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_advlock;
if (np->f_openDenyList) {
error = 0;
goto exit;
}
error = smbfs_smb_reopen_file(share, np, ap->a_context);
if (error) {
SMBDEBUG(" %s waiting to be revoked\n", np->n_name);
goto exit;
}
timo = (flags & F_WAIT) ? -1 : 0;
lck_pid = 1;
switch(ap->a_op) {
case F_SETLK:
if (! np->f_smbflock) {
error = smbfs_smb_lock(share, SMB_LOCK_EXCL, np->f_fid, lck_pid,
start, len, timo, ap->a_context);
if (error)
goto exit;
SMB_MALLOC(np->f_smbflock, struct smbfs_flock *, sizeof *np->f_smbflock,
M_LOCKF, M_WAITOK);
np->f_smbflock->refcnt = 1;
np->f_smbflock->fl_type = ap->a_fl->l_type;
np->f_smbflock->lck_pid = lck_pid;
np->f_smbflock->start = start;
np->f_smbflock->len = len;
np->f_smbflock->flck_pid = proc_pid(vfs_context_proc(ap->a_context));
} else if (np->f_smbflock->flck_pid == (uint32_t)proc_pid(vfs_context_proc(ap->a_context))) {
if ((np->f_smbflock->refcnt == 1) &&
(np->f_smbflock->fl_type != ap->a_fl->l_type)) {
np->f_smbflock->fl_type = ap->a_fl->l_type;
goto exit;
}
if (np->f_smbflock->fl_type != ap->a_fl->l_type) {
error = ENOTSUP;
goto exit;
}
if (np->f_smbflock->fl_type != F_WRLCK) {
} else {
np->f_smbflock->refcnt++;
}
} else {
error = EWOULDBLOCK;
goto exit;
}
break;
case F_UNLCK:
error = 0;
if (! np->f_smbflock)
break;
np->f_smbflock->refcnt--;
if (np->f_smbflock->refcnt <= 0) {
error = smbfs_smb_lock(share, SMB_LOCK_RELEASE, np->f_fid, lck_pid,
start, len, timo, ap->a_context);
if (error == 0) {
SMB_FREE(np->f_smbflock, M_LOCKF);
np->f_smbflock = NULL;
}
}
break;
default:
error = EINVAL;
break;
}
exit:
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_pathcheck(struct smb_share *share, const char *name, size_t nmlen,
uint32_t nameiop)
{
const char *cp, *endp;
int error;
if ((uint32_t)nmlen > share->ss_maxfilenamelen) {
if (SMB_UNICODE_STRINGS(SSTOVC(share))) {
uint16_t *convbuf;
size_t ntwrk_len;
SMB_MALLOC(convbuf, uint16_t *, nmlen * 2, M_SMBNODENAME, M_WAITOK);
if (! convbuf)
return ENAMETOOLONG;
ntwrk_len = smb_strtouni(convbuf, name, nmlen,
UTF_PRECOMPOSED | UTF_SFM_CONVERSIONS);
SMB_FREE(convbuf, M_SMBNODENAME);
if (ntwrk_len > (share->ss_maxfilenamelen * 2))
return ENAMETOOLONG;
} else {
return ENAMETOOLONG;
}
} else if (! nmlen) {
return ENAMETOOLONG;
}
if (nameiop == LOOKUP)
return (0);
if ((! UNIX_SERVER(SSTOVC(share))) && CON_FILENAME(name, nmlen)) {
if ((nmlen == 3) || ((nmlen > 3) && (*(name+3) == '.')))
return (ENAMETOOLONG);
}
if (SMB_UNICODE_STRINGS(SSTOVC(share)))
return (0);
error = ENAMETOOLONG;
for (cp = name, endp = name + nmlen; cp < endp; ++cp) {
switch (*cp) {
case 0x20:
case 0x2B:
case 0x2C:
case 0x3B:
case 0x3D:
case 0x5B:
case 0x5D:
break;
case 0x22:
case 0x2A:
case 0x2F:
case 0x3A:
case 0x3C:
case 0x3E:
case 0x3F:
case 0x5C:
case 0x7C:
return (error);
break;
default:
break;
}
}
return (0);
}
static int
smbfs_vnop_lookup(struct vnop_lookup_args *ap)
{
vfs_context_t context = ap->a_context;
vnode_t dvp = ap->a_dvp;
vnode_t *vpp = ap->a_vpp;
vnode_t vp;
struct smbnode *dnp = NULL;
struct mount *mp = vnode_mount(dvp);
struct smb_share *share = NULL;
struct componentname *cnp = ap->a_cnp;
const char *name = cnp->cn_nameptr;
uint32_t flags = cnp->cn_flags;
uint32_t nameiop = cnp->cn_nameiop;
size_t nmlen = cnp->cn_namelen;
struct smbfattr fattr, *fap = NULL;
int wantparent, error, islastcn, isdot = FALSE;
int parent_locked = FALSE;
if (!vnode_isdir(dvp))
return (ENOTDIR);
if ((flags & ISDOTDOT) && vnode_isvroot(dvp)) {
SMBFSERR("invalid '..'\n");
return (EIO);
}
islastcn = (flags & ISLASTCN) ? TRUE : FALSE;
if (islastcn && vfs_isrdonly(mp) && nameiop != LOOKUP)
return (EROFS);
wantparent = (flags & (LOCKPARENT|WANTPARENT)) ? TRUE : FALSE;
share = smb_get_share_with_reference(VTOSMBFS(dvp));
if (smbnode_lock(VTOSMB(dvp), SMBFS_EXCLUSIVE_LOCK) == 0) {
VTOSMB(dvp)->n_lastvop = smbfs_vnop_lookup;
if (VTOSMB(dvp)->n_flag & NNEGNCENTRIES) {
(void)smbfs_update_cache(share, dvp, NULL, context);
}
smbnode_unlock(VTOSMB(dvp));
}
*vpp = NULLVP;
error = cache_lookup(dvp, vpp, cnp);
switch (error) {
case ENOENT:
goto skipLookup;
case 0:
break;
case -1:
if (nameiop != CREATE) {
error = 0;
if (smbnode_lock(VTOSMB(*vpp), SMBFS_EXCLUSIVE_LOCK) == 0) {
VTOSMB(*vpp)->n_lastvop = smbfs_vnop_lookup;
error = smbfs_update_cache(share, *vpp, NULL, context);
smbnode_unlock(VTOSMB(*vpp));
}
if (error != ENOENT) {
error = 0;
goto done;
}
}
if (*vpp) {
cache_purge(*vpp);
vnode_put(*vpp);
*vpp = NULLVP;
}
break;
default:
SMBWARNING("cache_lookup error=%d\n", error);
goto done;
}
error = smbfs_pathcheck(share, cnp->cn_nameptr, cnp->cn_namelen, nameiop);
if (error) {
SMBWARNING("warning: bad filename %s\n", name);
goto done;
}
dnp = VTOSMB(dvp);
if (smbnode_lock(dnp, SMBFS_EXCLUSIVE_LOCK) != 0) {
error = ENOENT;
goto skipLookup;
}
parent_locked = TRUE;
dnp->n_lastvop = smbfs_vnop_lookup;
isdot = (nmlen == 1 && name[0] == '.');
fap = &fattr;
if (flags & ISDOTDOT) {
error = smbfs_lookup(share, dnp->n_parent, NULL, NULL, fap, context);
} else {
error = smbfs_lookup(share, dnp, &name, &nmlen, fap, context);
}
if ((error == ENOENT) && (cnp->cn_flags & MAKEENTRY) &&
(!(((nameiop == CREATE) || (nameiop == RENAME)) && islastcn))) {
cache_enter(dvp, NULL, cnp);
dnp->n_flag |= NNEGNCENTRIES;
}
skipLookup:
if (error) {
if (((nameiop == CREATE) || (nameiop == RENAME)) &&
(error == ENOENT) && islastcn) {
error = EJUSTRETURN;
}
} else if ((nameiop == RENAME) && islastcn && wantparent) {
if (isdot) {
error = EISDIR;
} else {
error = smbfs_nget(share, mp, dvp, name, nmlen, fap, &vp, 0, context);
if (!error) {
smbnode_unlock(VTOSMB(vp));
*vpp = vp;
}
}
} else if ((nameiop == DELETE) && islastcn) {
if (isdot) {
error = vnode_get(dvp);
if (!error)
*vpp = dvp;
} else {
error = smbfs_nget(share, mp, dvp, name, nmlen, fap, &vp, 0, context);
if (!error) {
smbnode_unlock(VTOSMB(vp));
*vpp = vp;
}
}
} else if (flags & ISDOTDOT) {
if (dvp && VTOSMB(dvp)->n_parent) {
vp = VTOSMB(dvp)->n_parent->n_vnode;
error = vnode_get(vp);
if (!error)
*vpp = vp;
}
} else if (isdot) {
error = vnode_get(dvp);
if (!error)
*vpp = dvp;
} else {
error = smbfs_nget(share, mp, dvp, name, nmlen, fap, &vp,
cnp->cn_flags, context);
if (!error) {
smbnode_unlock(VTOSMB(vp));
*vpp = vp;
}
}
if (name != cnp->cn_nameptr) {
SMB_FREE(name, M_SMBNODENAME);
}
if (parent_locked && dnp)
smbnode_unlock(dnp);
done:
smb_share_rele(share, context);
return (error);
}
static int
smbfs_vnop_offtoblk(struct vnop_offtoblk_args *ap)
{
*ap->a_lblkno = ap->a_offset / PAGE_SIZE_64;
return (0);
}
static int
smbfs_vnop_blktooff(struct vnop_blktooff_args *ap)
{
*ap->a_offset = (off_t)ap->a_lblkno * PAGE_SIZE_64;
return (0);
}
static int
smbfs_vnop_pagein(struct vnop_pagein_args *ap)
{
vnode_t vp = ap->a_vp;
struct smb_share *share;
size_t size = ap->a_size;
off_t f_offset = ap->a_f_offset;
struct smbnode *np;
int error;
np = VTOSMB(vp);
if ((size <= 0) || (f_offset < 0) || (f_offset >= (off_t)np->n_size) ||
(f_offset & PAGE_MASK_64) || (size & PAGE_MASK)) {
return err_pagein(ap);
}
share = smb_get_share_with_reference(VTOSMBFS(vp));
error = smbfs_smb_reopen_file(share, np, ap->a_context);
if (error) {
SMBDEBUG(" %s waiting to be revoked\n", np->n_name);
smb_share_rele(share, ap->a_context);
error = err_pagein(ap);
return error;
}
error = cluster_pagein(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
(int)ap->a_size, (off_t)np->n_size, ap->a_flags);
if (error) {
SMB_LOG_IO("%s failed cluster_pagein with an error of %d\n", np->n_name, error);
}
smb_share_rele(share, ap->a_context);
return (error);
}
static int
smbfs_vnop_pageout(struct vnop_pageout_args *ap)
{
vnode_t vp = ap->a_vp;
struct smbnode *np;
struct smb_share *share;
upl_t pl = ap->a_pl;
size_t size = ap->a_size;
off_t f_offset = ap->a_f_offset;
int error;
if (vnode_vfsisrdonly(vp))
return(EROFS);
np = VTOSMB(vp);
if (pl == (upl_t)NULL)
panic("smbfs_vnop_pageout: no upl");
if ((size <= 0) || (f_offset < 0) || (f_offset >= (off_t)np->n_size) ||
(f_offset & PAGE_MASK_64) || (size & PAGE_MASK)) {
return err_pageout(ap);
}
share = smb_get_share_with_reference(VTOSMBFS(vp));
error = smbfs_smb_reopen_file(share, np, ap->a_context);
if (error) {
SMBDEBUG(" %s waiting to be revoked\n", np->n_name);
smb_share_rele(share, ap->a_context);
return err_pageout(ap);
}
error = cluster_pageout(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
(int)ap->a_size, (off_t)np->n_size, ap->a_flags);
if (error) {
SMB_LOG_IO("%s failed cluster_pageout with an error of %d\n",
np->n_name, error);
}
smb_share_rele(share, ap->a_context);
if (vnode_isnamedstream(vp)) {
vnode_t parent_vp = vnode_getparent(vp);
if (parent_vp) {
VTOSMB(parent_vp)->attribute_cache_timer = 0;
vnode_put(parent_vp);
}
}
return (error);
}
static uint32_t emptyfinfo[8] = {0};
static void
DefaultFillAfpInfo(uint8_t *afpinfo)
{
int ii = 0;
bzero(afpinfo, AFP_INFO_SIZE);
afpinfo[ii++] = 'A';
afpinfo[ii++] = 'F';
afpinfo[ii++] = 'P';
afpinfo[ii++] = 0;
afpinfo[ii++] = 0;
afpinfo[ii++] = 0;
afpinfo[ii++] = 0x01;
afpinfo[ii++] = 0;
ii += 4;
afpinfo[ii++] = 0;
afpinfo[ii++] = 0;
afpinfo[ii++] = 0;
afpinfo[ii] = 0x80;
}
static const char *
xattr2sfm(const char *xa, enum stream_types *stype)
{
if (!bcmp(xa, SFM_RESOURCEFORK_NAME, sizeof(SFM_RESOURCEFORK_NAME))) {
return(NULL);
}
if (!bcmp(xa, SFM_FINDERINFO_NAME, sizeof(SFM_FINDERINFO_NAME))) {
return(NULL);
}
if (!bcmp(xa, SFM_DESKTOP_NAME, sizeof(SFM_DESKTOP_NAME))) {
return(NULL);
}
if (!bcmp(xa, SFM_IDINDEX_NAME, sizeof(SFM_IDINDEX_NAME))) {
return(NULL);
}
if (!bcmp(xa, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME))) {
*stype = kResourceFrk;
return (SFM_RESOURCEFORK_NAME);
}
if (!bcmp(xa, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME))) {
*stype = kFinderInfo;
return (SFM_FINDERINFO_NAME);
}
*stype = kExtendedAttr;
return (xa);
}
static int
smbfs_vnop_setxattr(struct vnop_setxattr_args *ap)
{
vnode_t vp = ap->a_vp;
const char *sfmname;
int error = 0;
uint16_t fid = 0;
uint32_t rights = SMB2_FILE_WRITE_DATA;
struct smbnode *np = NULL;
struct smb_share *share = NULL;
enum stream_types stype = kNoStream;
uint32_t open_disp = 0;
uio_t afp_uio = NULL;
uint8_t afpinfo[60];
struct smbfattr fattr;
DBG_ASSERT(!vnode_isnamedstream(vp));
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_setxattr;
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (!(share->ss_attributes & FILE_NAMED_STREAMS)) {
error = ENOTSUP;
goto exit;
}
if ( (ap->a_options & XATTR_CREATE) && (ap->a_options & XATTR_REPLACE) ) {
error = EINVAL;
goto exit;
}
if (strchr(ap->a_name, '/')) {
error = EINVAL;
SMBWARNING("Slash in xattr name not allowed: error %d %s:%s\n", error,
np->n_name, ap->a_name);
goto exit;
}
sfmname = xattr2sfm(ap->a_name, &stype);
if (!sfmname) {
error = EINVAL;
goto exit;
}
if ((stype & kResourceFrk) != kResourceFrk)
rights |= SMB2_FILE_WRITE_ATTRIBUTES;
if (stype & kFinderInfo) {
uint8_t finfo[FINDERINFOSIZE];
time_t attrtimeo;
struct timespec ts;
size_t sizep;
int len = (int)uio_resid(ap->a_uio);
if (len > FINDERINFOSIZE) {
error = EINVAL;
goto exit;
}
error = uiomove((void *)finfo, len, ap->a_uio);
if (error)
goto exit;
SMB_CACHE_TIME(ts, np, attrtimeo);
if ((ts.tv_sec - np->finfo_cache) <= attrtimeo) {
if (bcmp(np->finfo, finfo, sizeof(finfo)) == 0)
goto exit;
}
rights |= SMB2_FILE_READ_DATA;
afp_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
if (afp_uio)
error = uio_addiov( afp_uio, CAST_USER_ADDR_T(afpinfo), sizeof(afpinfo));
else
error = ENOMEM;
if (error)
goto exit;
DefaultFillAfpInfo(afpinfo);
error = smbfs_smb_openread(share, np, &fid, rights, afp_uio, &sizep,
sfmname, &ts, ap->a_context);
if (!error) {
bcopy((void *)finfo, (void *)&afpinfo[AFP_INFO_FINDER_OFFSET], len);
uio_reset(afp_uio, 0, UIO_SYSSPACE, UIO_WRITE );
error = uio_addiov( afp_uio, CAST_USER_ADDR_T(afpinfo), sizeof(afpinfo));
}
if (error)
goto out;
if (sizep && (VTOSMBFS(vp)->sm_flags & MNT_IS_SFM_VOLUME)) {
(void)smbfs_seteof(share, fid, 0, ap->a_context);
}
if (!error) {
error = smb_write(share, fid, afp_uio, 0, ap->a_context);
}
(void)smbfs_smb_setfattrNT(share, (np->n_dosattr & ~SMB_EFA_DIRECTORY),
fid, NULL, &ts, NULL, ap->a_context);
if (!error) {
nanouptime(&ts);
np->finfo_cache = ts.tv_sec;
bcopy((void *)&afpinfo[AFP_INFO_FINDER_OFFSET], np->finfo,
sizeof(np->finfo));
}
goto out;
}
switch(ap->a_options & (XATTR_CREATE | XATTR_REPLACE)) {
case XATTR_CREATE:
open_disp = FILE_CREATE;
break;
case XATTR_REPLACE:
open_disp = FILE_OVERWRITE;
break;
default:
if ((stype & kResourceFrk) == kResourceFrk) {
open_disp = FILE_OPEN_IF;
} else {
open_disp = FILE_OVERWRITE_IF;
}
break;
}
error = smbfs_smb_create(share, np, sfmname,
strnlen(sfmname, share->ss_maxfilenamelen+1),
rights, &fid, open_disp, 1, &fattr, ap->a_context);
if (error) {
goto exit;
}
error = smb_write(share, fid, ap->a_uio, 0, ap->a_context);
if ((stype & kResourceFrk) != kResourceFrk) {
(void)smbfs_smb_setfattrNT(share, (np->n_dosattr & ~SMB_EFA_DIRECTORY),
fid, NULL, &np->n_mtime, NULL, ap->a_context);
}
out:
if (fid)
(void)smbfs_smb_close(share, fid, ap->a_context);
exit:
if (afp_uio)
uio_free(afp_uio);
if (error == ENOENT)
error = ENOATTR;
if (error && (error != ENOTSUP) && (error != ENOATTR)) {
SMBWARNING("error %d %s:%s\n", error, np->n_name, ap->a_name);
if ((error != EROFS) && (error != EPERM) && (error != EINVAL) &&
(error != ENOTDIR) && (error != EACCES) && (error != ELOOP) &&
(error != EFAULT) && (error != EIO) && (error != ENAMETOOLONG) &&
(error != EEXIST) && (error != ERANGE) &&
(error != E2BIG) && (error != ENOSPC))
error = EIO;
}
if (!error) {
np->n_fstatus &= ~kNO_SUBSTREAMS;
}
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_listxattr(struct vnop_listxattr_args *ap)
{
vnode_t vp = ap->a_vp;
uio_t uio = ap->a_uio;
size_t *sizep = ap->a_size;
struct smbnode *np = NULL;
struct smb_share *share = NULL;
int error = 0;
DBG_ASSERT(!vnode_isnamedstream(vp));
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_listxattr;
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (!(share->ss_attributes & FILE_NAMED_STREAMS)) {
error = ENOTSUP;
goto exit;
}
if (np->n_fstatus & kNO_SUBSTREAMS) {
error = ENOATTR;
goto exit;
}
error = smbfs_smb_qstreaminfo(share, np, uio, sizep, NULL, NULL,
ap->a_context);
exit:
if (error == ENOATTR)
error = 0;
if (error && (error != ENOTSUP)) {
SMBWARNING("error %d %s\n", error, np->n_name);
if ((error != ERANGE) && (error != EPERM) && (error != EINVAL) &&
(error != ENOTDIR) && (error != EACCES) && (error != ELOOP) &&
(error != EFAULT) && (error != EIO))
error = 0;
}
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_removexattr(struct vnop_removexattr_args *ap)
{
vnode_t vp = ap->a_vp;
const char *sfmname;
int error = 0;
struct smbnode *np = NULL;
struct smb_share *share = NULL;
enum stream_types stype = kNoStream;
struct timespec ts;
DBG_ASSERT(!vnode_isnamedstream(vp));
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_removexattr;
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (!(share->ss_attributes & FILE_NAMED_STREAMS)) {
error = ENOTSUP;
goto exit;
}
if (strchr(ap->a_name, '/')) {
error = EINVAL;
SMBWARNING("Slash in xattr name not allowed: error %d %s:%s\n", error,
np->n_name, ap->a_name);
goto exit;
}
sfmname = xattr2sfm(ap->a_name, &stype);
if (!sfmname) {
error = EINVAL;
goto exit;
}
if (stype & kFinderInfo) {
if (VTOSMBFS(vp)->sm_flags & MNT_IS_SFM_VOLUME) {
error = ENOTSUP;
} else {
error = smbfs_smb_delete(share, np, sfmname,
strnlen(sfmname, share->ss_maxfilenamelen+1),
1, ap->a_context);
}
if ((error == ENOTSUP) || (error == EINVAL)) {
uio_t afp_uio = NULL;
uint8_t afpinfo[60];
uint16_t fid = 0;
uint32_t rights = SMB2_FILE_WRITE_DATA | SMB2_FILE_READ_DATA | SMB2_FILE_WRITE_ATTRIBUTES;
afp_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
if (!afp_uio) {
error = ENOMEM;
goto exit;
}
error = uio_addiov( afp_uio, CAST_USER_ADDR_T(afpinfo), sizeof(afpinfo));
if (error) {
uio_free(afp_uio);
goto exit;
}
error = smbfs_smb_openread(share, np, &fid, rights, afp_uio, NULL,
sfmname, &ts, ap->a_context);
bzero(&afpinfo[AFP_INFO_FINDER_OFFSET], FINDERINFOSIZE);
if (!error)
error = smbfs_seteof(share, fid, 0, ap->a_context);
if (!error) {
uio_reset(afp_uio, 0, UIO_SYSSPACE, UIO_WRITE );
error = uio_addiov( afp_uio, CAST_USER_ADDR_T(afpinfo), sizeof(afpinfo));
}
if (!error)
error = smb_write(share, fid, afp_uio, 0, ap->a_context);
(void)smbfs_smb_setfattrNT(share, np->n_dosattr, fid, NULL, &ts,
NULL, ap->a_context);
if (fid)
(void)smbfs_smb_close(share, fid, ap->a_context);
if (afp_uio) {
uio_free(afp_uio);
}
}
} else {
error = smbfs_smb_delete(share, np, sfmname,
strnlen(sfmname, share->ss_maxfilenamelen+1),
1, ap->a_context);
}
if ((stype & kFinderInfo) && !error) {
nanouptime(&ts);
np->finfo_cache = ts.tv_sec;
bzero(np->finfo, sizeof(np->finfo));
}
exit:
if (error == ENOENT)
error = ENOATTR;
if (error && (error != ENOTSUP) && (error != ENOATTR)) {
SMBWARNING("error %d %s:%s\n", error, np->n_name, ap->a_name);
if ((error != EROFS) && (error != EPERM) && (error != EINVAL) &&
(error != ENOTDIR) && (error != EACCES) && (error != ELOOP) &&
(error != EFAULT) && (error != EIO) && (error != ENAMETOOLONG))
error = ENOATTR;
}
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_getxattr(struct vnop_getxattr_args *ap)
{
vnode_t vp = ap->a_vp;
const char *sfmname;
uio_t uio = ap->a_uio;
size_t *sizep = ap->a_size;
uint16_t fid = 0;
int error = 0;
struct smbnode *np = NULL;
struct smb_share *share = NULL;
size_t rq_resid = (uio) ? (size_t)uio_resid(uio) : 0;
uio_t afp_uio = NULL;
enum stream_types stype = kNoStream;
struct timespec ts;
time_t attrtimeo;
DBG_ASSERT(!vnode_isnamedstream(vp));
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_getxattr;
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (!(share->ss_attributes & FILE_NAMED_STREAMS)) {
error = ENOTSUP;
goto exit;
}
if (strchr(ap->a_name, '/')) {
error = ENOTSUP;
SMBWARNING("Slash in xattr name not allowed: error %d %s:%s\n", error,
np->n_name, ap->a_name);
goto exit;
}
if (np->n_fstatus & kNO_SUBSTREAMS) {
error = ENOATTR;
goto exit;
}
sfmname = xattr2sfm(ap->a_name, &stype);
if (!sfmname) {
error = EINVAL;
goto exit;
}
if ((uio == NULL) && !(stype & kFinderInfo)) {
uint64_t strmsize = 0;
if (stype & kResourceFrk) {
error = smb_get_rsrcfrk_size(share, vp, ap->a_context);
lck_mtx_lock(&np->rfrkMetaLock);
strmsize = np->rfrk_size;
lck_mtx_unlock(&np->rfrkMetaLock);
}
else {
error = smbfs_smb_qstreaminfo(share, np, NULL, NULL, sfmname,
&strmsize, ap->a_context);
}
if (sizep)
*sizep = (size_t)strmsize;
if (error)
error = ENOATTR;
goto exit;
}
if (stype & kFinderInfo) {
SMB_CACHE_TIME(ts, np, attrtimeo);
if ((ts.tv_sec - np->finfo_cache) > attrtimeo) {
size_t afpsize = 0;
uint8_t afpinfo[60];
afp_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
if (afp_uio)
error = uio_addiov( afp_uio, CAST_USER_ADDR_T(afpinfo),
sizeof(afpinfo));
else error = ENOMEM;
if (error)
goto exit;
uio_setoffset(afp_uio, 0);
error = smbfs_smb_openread(share, np, &fid, SMB2_FILE_READ_DATA,
afp_uio, &afpsize, sfmname, NULL,
ap->a_context);
if (afpsize != AFP_INFO_SIZE)
error = ENOENT;
if (error == ENOENT) {
bzero(np->finfo, sizeof(np->finfo));
} else {
bcopy((void *)&afpinfo[AFP_INFO_FINDER_OFFSET], np->finfo,
sizeof(np->finfo));
}
if (vnode_isreg(vp) && (bcmp(np->finfo, "brokMACS", 8) == 0)) {
np->finfo_cache = 0;
SMBDEBUG("Don't cache finder info, we have a finder copy in progress\n");
} else {
nanouptime(&ts);
np->finfo_cache = ts.tv_sec;
}
}
if ((!(VTOSMBFS(vp)->sm_flags & MNT_IS_SFM_VOLUME)) &&
(bcmp(np->finfo, emptyfinfo, sizeof(emptyfinfo)) == 0)) {
error = ENOENT;
}
if (uio && !error) {
error = uiomove((const char *)np->finfo, (int)sizeof(np->finfo), ap->a_uio);
}
if (sizep && !error)
*sizep = FINDERINFOSIZE;
} else {
error = smbfs_smb_openread(share, np, &fid, SMB2_FILE_READ_DATA, uio,
sizep, sfmname, NULL, ap->a_context);
}
if (error != ENOTSUP)
goto out;
error = smbfs_smb_open_xattr(share, np, SMB2_FILE_READ_DATA,
NTCREATEX_SHARE_ACCESS_ALL, &fid,
sfmname, sizep, ap->a_context);
if (error)
goto exit;
if (stype & kFinderInfo) {
user_ssize_t r;
if (sizep)
*sizep = FINDERINFOSIZE;
if (uio == NULL)
goto out;
r = uio_resid(uio);
if (uio_offset(uio) >= FINDERINFOSIZE) {
uio_setresid(uio, 0);
} else if (uio_offset(uio) + r > FINDERINFOSIZE)
uio_setresid(uio, FINDERINFOSIZE - uio_offset(uio));
r = r - uio_resid(uio);
uio_setoffset(uio, uio_offset(uio) + 4*4);
error = smb_read(share, fid, uio, ap->a_context);
uio_setoffset(uio, uio_offset(uio) - 4*4);
uio_setresid(uio, uio_resid(uio) + r);
}
else error = smb_read(share, fid, uio, ap->a_context);
out:;
if (uio && sizep && (*sizep > rq_resid))
error = ERANGE;
if (fid)
(void)smbfs_smb_close(share, fid, ap->a_context);
exit:
if ((error == ENOENT) || ((error == EISDIR) && (stype & kFinderInfo)))
error = ENOATTR;
if (afp_uio)
uio_free(afp_uio);
if (error && (error != ENOTSUP) && (error != ENOATTR)) {
SMBWARNING("error %d %s:%s\n", error, np->n_name, ap->a_name);
if ((error != ERANGE) && (error != EPERM) && (error != EINVAL) &&
(error != EISDIR) && (error != ENOTDIR) && (error != EACCES) &&
(error != ELOOP) && (error != EFAULT) && (error != EIO))
error = ENOATTR;
}
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_getnamedstream(struct vnop_getnamedstream_args* ap)
{
struct smb_share *share;
vnode_t vp = ap->a_vp;
vnode_t *svpp = ap->a_svpp;
const char * streamname = ap->a_name;
const char * sname = ap->a_name;
struct smbnode *np = NULL;
int error = 0;
uint64_t strmsize = 0;
struct smbfattr fattr;
struct vnode_attr vap;
struct timespec ts;
time_t attrtimeo;
struct timespec reqtime;
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
nanouptime(&reqtime);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_getnamedstream;
share = smb_get_share_with_reference(VTOSMBFS(vp));
*svpp = NULL;
if (bcmp(streamname, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
SMBDEBUG("Wrong stream %s:$%s\n", np->n_name, streamname);
error = ENOATTR;
goto exit;
} else {
sname = SFM_RESOURCEFORK_NAME;
}
if ( !vnode_isreg(vp) ) {
SMBDEBUG("%s not a file (EPERM)\n", np->n_name);
error = EPERM;
goto exit;
}
VATTR_INIT(&vap);
error = smbfs_getattr(share, vp, &vap, ap->a_context);
if (error) {
SMBERROR("%s lookup failed %d\n", np->n_name, error);
goto exit;
}
if ((*svpp = smbfs_find_vgetstrm(VTOSMBFS(vp), np, sname,
share->ss_maxfilenamelen)) != NULL) {
VTOSMB(*svpp)->n_mtime = np->n_mtime;
SMB_CACHE_TIME(ts, VTOSMB(*svpp), attrtimeo);
if ((ts.tv_sec - VTOSMB(*svpp)->attribute_cache_timer) <= attrtimeo)
goto exit;
}
if ((smbfs_smb_qstreaminfo(share, np, NULL, NULL, sname,
&strmsize, ap->a_context)) && (*svpp == NULL)) {
error = ENOATTR;
goto exit;
}
if (*svpp) {
if (smbfs_update_size(VTOSMB(*svpp), &reqtime, strmsize) == TRUE) {
nanouptime(&ts);
VTOSMB(*svpp)->attribute_cache_timer = ts.tv_sec;
}
goto exit;
}
bzero(&fattr, sizeof(fattr));
fattr.fa_vtype = VREG;
fattr.fa_size = strmsize;
fattr.fa_data_alloc = 0;
fattr.fa_attr = np->n_dosattr;
fattr.fa_atime = np->n_atime;
fattr.fa_chtime = np->n_chtime;
fattr.fa_mtime = np->n_mtime;
fattr.fa_crtime = np->n_crtime;
nanouptime(&fattr.fa_reqtime);
error = smbfs_vgetstrm(share, VTOSMBFS(vp), vp, svpp, &fattr, sname);
exit:
if (*svpp)
smbnode_unlock(VTOSMB(*svpp));
if (error && (error != ENOATTR)) {
SMBWARNING(" %s:$%s Original Stream name %s error = %d\n", np->n_name,
sname, streamname, error);
}
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_makenamedstream(struct vnop_makenamedstream_args* ap)
{
struct smb_share *share;
vnode_t vp = ap->a_vp;
vnode_t *svpp = ap->a_svpp;
const char * streamname = ap->a_name;
struct smbnode *np = NULL;
int error = 0;
struct smbfattr fattr;
struct timespec ts;
int rsrcfrk = FALSE;
size_t max_name_len;
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_makenamedstream;
share = smb_get_share_with_reference(VTOSMBFS(vp));
*svpp = NULL;
if (bcmp(streamname, XATTR_RESOURCEFORK_NAME,
sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
SMBDEBUG("Wrong stream %s:$%s\n", np->n_name, streamname);
error = ENOATTR;
goto exit;
} else {
max_name_len = sizeof(XATTR_RESOURCEFORK_NAME);
streamname = SFM_RESOURCEFORK_NAME;
}
if ( !vnode_isreg(vp) ) {
SMBDEBUG("%s not a file (EPERM)\n", np->n_name);
error = EPERM;
goto exit;
}
error = smbfs_smb_create(share, np, streamname, max_name_len,
SMB2_FILE_WRITE_DATA, NULL,
FILE_OPEN_IF, 1, &fattr, ap->a_context);
if (error)
goto exit;
np->n_fstatus &= ~kNO_SUBSTREAMS;
error = smbfs_vgetstrm(share, VTOSMBFS(vp), vp, svpp, &fattr, streamname);
if (error == 0) {
if (rsrcfrk) {
lck_mtx_lock(&np->rfrkMetaLock);
np->rfrk_size = fattr.fa_size;
nanouptime(&ts);
np->rfrk_cache_timer = ts.tv_sec;
lck_mtx_unlock(&np->rfrkMetaLock);
}
smbnode_unlock(VTOSMB(*svpp));
}
exit:
if (error)
SMBWARNING(" %s:$%s error = %d\n", np->n_name, streamname, error);
smb_share_rele(share, ap->a_context);
smbnode_unlock(np);
return (error);
}
static int
smbfs_vnop_removenamedstream(struct vnop_removenamedstream_args* ap)
{
vnode_t vp = ap->a_vp;
vnode_t svp = ap->a_svp;
const char * streamname = ap->a_name;
struct smbnode *np = NULL;
int error = 0;
size_t max_name_len;
struct smb_share *share = NULL;
if ((error = smbnode_lockpair(VTOSMB(vp), VTOSMB(svp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(svp);
np->n_lastvop = smbfs_vnop_removenamedstream;
share = smb_get_share_with_reference(VTOSMBFS(vp));
if (bcmp(streamname, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
SMBDEBUG("Wrong stream %s:$%s\n", np->n_name, streamname);
error = ENOATTR;
goto exit;
} else {
max_name_len = sizeof(XATTR_RESOURCEFORK_NAME);
streamname = SFM_RESOURCEFORK_NAME;
}
if ( !vnode_isreg(vp) ) {
SMBDEBUG("%s not a file (EPERM)\n", np->n_name);
error = EPERM;
goto exit;
}
error = smbfs_smb_delete(share, np, streamname, max_name_len, TRUE,
ap->a_context);
if (!error)
smb_vhashrem(np);
exit:
if (error)
SMBWARNING(" %s:$%s error = %d\n", np->n_name, streamname, error);
smb_share_rele(share, ap->a_context);
smbnode_unlockpair(VTOSMB(vp), VTOSMB(svp));
return (error);
}
static int
smbfs_vnop_monitor(struct vnop_monitor_args *ap)
{
struct smbnode *np;
struct smb_share *share = NULL;
int error = 0;
int releaseLock = TRUE;
if (! vnode_isdir(ap->a_vp)) {
SMBDEBUG("%s is not a directory (ENOTSUP): node type = 0x%0x a_events = 0x%x, a_flags = 0x%x, a_handle = %p\n",
VTOSMB(ap->a_vp)->n_name, vnode_vtype(ap->a_vp),
ap->a_events, ap->a_flags, ap->a_handle);
return ENOTSUP;
}
if ((error = smbnode_lock(VTOSMB(ap->a_vp), SMBFS_EXCLUSIVE_LOCK)))
return (error);
np = VTOSMB(ap->a_vp);
np->n_lastvop = smbfs_vnop_monitor;
share = smb_get_share_with_reference(VTOSMBFS(ap->a_vp));
SMBDEBUG("%s a_events = 0x%x, a_flags = 0x%x, a_handle = %p\n",
np->n_name, ap->a_events, ap->a_flags, ap->a_handle);
switch (ap->a_flags) {
case VNODE_MONITOR_BEGIN:
error = smbfs_start_change_notify(share, np, ap->a_context,
&releaseLock);
break;
case VNODE_MONITOR_END:
error = smbfs_stop_change_notify(share, np, FALSE, ap->a_context,
&releaseLock);
break;
case VNODE_MONITOR_UPDATE:
default:
error = ENOTSUP;
break;
}
smb_share_rele(share, ap->a_context);
if (releaseLock)
smbnode_unlock(VTOSMB(ap->a_vp));
return error;
}
static int
smbfs_vnop_access(struct vnop_access_args *ap)
{
vnode_t vp = ap->a_vp;
int32_t action = ap->a_action, write_rights;
vfs_context_t context = ap->a_context;
kauth_cred_t cred = vfs_context_ucred(context);
struct smbmount *smp = VTOSMBFS(vp);
struct smb_share *share;
uint32_t maxAccessRights;
int error = 0;
share = smb_get_share_with_reference(VTOSMBFS(vp));
if ((vfs_context_suser(context) != 0) &&
(kauth_cred_getuid(cred) != smp->sm_args.uid) &&
!SMBV_HAS_GUEST_ACCESS(SSTOVC(share))) {
SMB_LOG_ACCESS("%d not authorized to access %s : action = 0x%x\n",
kauth_cred_getuid(cred), VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if (((action & KAUTH_VNODE_ACCESS) != KAUTH_VNODE_ACCESS) &&
(((action & KAUTH_VNODE_EXECUTE) != KAUTH_VNODE_EXECUTE) ||
(!vnode_isreg(vp)))) {
goto done;
}
write_rights = KAUTH_VNODE_WRITE_RIGHTS;
write_rights &= ~(KAUTH_VNODE_WRITE_ATTRIBUTES | KAUTH_VNODE_WRITE_EXTATTRIBUTES);
write_rights &= ~KAUTH_VNODE_CHECKIMMUTABLE;
if (node_isimmutable(share, vp) && (action & write_rights)) {
SMBDEBUG("%s action = 0x%x %s denied\n", VTOSMB(vp)->n_name, action,
vnode_isdir(vp) ? "IMMUTABLE_DIR" : "IMMUTABLE_FILE");
error = EPERM;
goto done;
}
if ((share->ss_fstype == SMB_FS_FAT) &&
((share->ss_attributes & FILE_PERSISTENT_ACLS) != FILE_PERSISTENT_ACLS)) {
SMBDEBUG("FAT: Access call not supported by server\n");
goto done;
}
#ifdef SMB_DEBUG_ACCESS
if (SMBV_HAS_GUEST_ACCESS(SSTOVC(share))) {
SMBDEBUG("SMBV_GUEST_ACCESS: %s action = 0x%x\n",
VTOSMB(vp)->n_name, action);
} else {
SMBDEBUG("%s action = 0x%x\n", VTOSMB(vp)->n_name, action);
}
#endif // SMB_DEBUG_ACCESS
if (vnode_isnamedstream(vp)) {
vnode_t parent_vp = vnode_getparent(vp);
if (!parent_vp)
return 0;
maxAccessRights = smbfs_get_maximum_access(share, parent_vp, context);
vnode_put(parent_vp);
} else {
maxAccessRights = smbfs_get_maximum_access(share, vp, context);
}
if ((action & KAUTH_VNODE_READ_DATA) &&
((maxAccessRights & SMB2_FILE_READ_DATA) != SMB2_FILE_READ_DATA)) {
SMBDEBUG("%s action = 0x%x %s denied\n", VTOSMB(vp)->n_name, action,
vnode_isdir(vp) ? "KAUTH_VNODE_LIST_DIRECTORY" : "KAUTH_VNODE_READ_DATA");
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_WRITE_DATA) &&
((maxAccessRights & SMB2_FILE_WRITE_DATA) != SMB2_FILE_WRITE_DATA)) {
SMBDEBUG("%s action = 0x%x %s denied\n", VTOSMB(vp)->n_name, action,
vnode_isdir(vp) ? "KAUTH_VNODE_ADD_FILE" : "KAUTH_VNODE_WRITE_DATA");
error = EACCES;
goto done;
}
if (action & KAUTH_VNODE_EXECUTE) {
if (vnode_isdir(vp) &&
((maxAccessRights & SMB2_FILE_TRAVERSE) != SMB2_FILE_TRAVERSE) &&
((maxAccessRights & SMB2_FILE_LIST_DIRECTORY) != SMB2_FILE_LIST_DIRECTORY)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_SEARCH denied\n", VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
} else if (!vnode_isdir(vp) &&
((maxAccessRights & SMB2_FILE_EXECUTE) != SMB2_FILE_EXECUTE)) {
if (((action & KAUTH_VNODE_ACCESS) != KAUTH_VNODE_ACCESS) &&
((maxAccessRights & SMB2_FILE_READ_DATA) == SMB2_FILE_READ_DATA)) {
goto done;
}
SMBDEBUG("%s action = 0x%x SMB2_FILE_EXECUTE denied\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
}
if ((action & KAUTH_VNODE_DELETE) &&
((maxAccessRights & SMB2_DELETE) != SMB2_DELETE)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_DELETE denied\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_APPEND_DATA) &&
((maxAccessRights & SMB2_FILE_APPEND_DATA) != SMB2_FILE_APPEND_DATA)) {
SMBDEBUG("%s action = 0x%x %s denied\n", VTOSMB(vp)->n_name, action,
vnode_isdir(vp) ? "KAUTH_VNODE_ADD_SUBDIRECTORY" : "KAUTH_VNODE_APPEND_DATA");
error = EACCES;
goto done;
}
#ifdef SMB_DEBUG_ACCESS
if ((action & KAUTH_VNODE_DELETE_CHILD) &&
((maxAccessRights & SMB2_FILE_DELETE_CHILD) != SMB2_FILE_DELETE_CHILD)) {
SMB_LOG_ACCESS("%s action = 0x%x 0x%x KAUTH_VNODE_DELETE_CHILD should denied\n",
VTOSMB(vp)->n_name, action, KAUTH_VNODE_DELETE_CHILD);
}
if ((action & KAUTH_VNODE_READ_ATTRIBUTES) &&
((maxAccessRights & SMB2_FILE_READ_ATTRIBUTES) != SMB2_FILE_READ_ATTRIBUTES)) {
SMB_LOG_ACCESS("%s action = 0x%x 0x%x KAUTH_VNODE_READ_ATTRIBUTES should denied\n",
VTOSMB(vp)->n_name, action, KAUTH_VNODE_READ_ATTRIBUTES);
}
#endif // SMB_DEBUG_ACCESS
if ((action & KAUTH_VNODE_WRITE_ATTRIBUTES) &&
((maxAccessRights & SMB2_FILE_WRITE_ATTRIBUTES) != SMB2_FILE_WRITE_ATTRIBUTES)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_WRITE_ATTRIBUTES denied\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_READ_EXTATTRIBUTES) &&
((maxAccessRights & SMB2_FILE_READ_ATTRIBUTES) != SMB2_FILE_READ_ATTRIBUTES)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_READ_EXTATTRIBUTES denied\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_WRITE_EXTATTRIBUTES) &&
((maxAccessRights & SMB2_FILE_WRITE_ATTRIBUTES) != SMB2_FILE_WRITE_ATTRIBUTES)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_WRITE_EXTATTRIBUTES denied\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_READ_SECURITY) &&
((maxAccessRights & SMB2_READ_CONTROL) != SMB2_READ_CONTROL)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_READ_SECURITY denied\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_WRITE_SECURITY) &&
((share->maxAccessRights & SMB2_WRITE_DAC) != SMB2_WRITE_DAC)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_WRITE_SECURITY denied by Share ACL\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_TAKE_OWNERSHIP) &&
((share->maxAccessRights & SMB2_WRITE_OWNER) != SMB2_WRITE_OWNER)) {
SMBDEBUG("%s action = 0x%x SHARE KAUTH_VNODE_TAKE_OWNERSHIP by Share ACL\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_WRITE_SECURITY) &&
((maxAccessRights & SMB2_WRITE_DAC) != SMB2_WRITE_DAC)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_WRITE_SECURITY denied\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
if ((action & KAUTH_VNODE_TAKE_OWNERSHIP) &&
((maxAccessRights & SMB2_WRITE_OWNER) != SMB2_WRITE_OWNER)) {
SMBDEBUG("%s action = 0x%x KAUTH_VNODE_TAKE_OWNERSHIP denied\n",
VTOSMB(vp)->n_name, action);
error = EACCES;
goto done;
}
done:
if (error) {
SMB_LOG_ACCESS("%s action = 0x%x denied\n", VTOSMB(vp)->n_name, action);
}
smb_share_rele(share, ap->a_context);
return error;
}
static int
smbfs_vnop_allocate(struct vnop_allocate_args *ap)
{
vnode_t vp = ap->a_vp;
u_int64_t length = (u_int64_t)ap->a_length;
struct smbnode *np;
int32_t error = 0;
uint16_t fid = 0;
*(ap->a_bytesallocated) = 0;
if (!vnode_isreg(vp))
return (EISDIR);
if ((error = smbnode_lock(VTOSMB(vp), SMBFS_EXCLUSIVE_LOCK))) {
return (error);
}
np = VTOSMB(vp);
np->n_lastvop = smbfs_vnop_allocate;
if ((ap->a_flags & ALLOCATEFROMVOL) && (length < np->n_size)) {
error = EINVAL;
goto done;
}
if (ap->a_flags & ALLOCATEFROMPEOF) {
if (length > (UINT32_MAX - np->n_size)) {
error = EINVAL;
goto done;
}
length += np->n_size;
}
if (FindFileRef(vp, vfs_context_proc(ap->a_context), kAccessWrite,
kAnyMatch, 0, 0, NULL, &fid)) {
fid = np->f_fid;
}
if (!fid) {
error = EBADF;
goto done;
}
if (!length || (np->n_size == length)) {
length = 0;
} else {
struct smb_share *share;
share = smb_get_share_with_reference(VTOSMBFS(vp));
length = roundup(length, VTOSMBFS(vp)->sm_statfsbuf.f_bsize);
error = smbfs_set_allocation(share, fid, length, ap->a_context);
smb_share_rele(share, ap->a_context);
}
if (!error) {
*(ap->a_bytesallocated) = length;
}
done:
if (error) {
SMBWARNING("%s: length = %lld, error = %d\n", np->n_name, length, error);
}
smbnode_unlock(VTOSMB(ap->a_vp));
return error;
}
vnop_t **smbfs_vnodeop_p;
static struct vnodeopv_entry_desc smbfs_vnodeop_entries[] = {
{ &vnop_default_desc, (vnop_t *) vn_default_error },
{ &vnop_advlock_desc, (vnop_t *) smbfs_vnop_advlock },
{ &vnop_close_desc, (vnop_t *) smbfs_vnop_close },
{ &vnop_create_desc, (vnop_t *) smbfs_vnop_create },
{ &vnop_fsync_desc, (vnop_t *) smbfs_vnop_fsync },
{ &vnop_getattr_desc, (vnop_t *) smbfs_vnop_getattr },
{ &vnop_pagein_desc, (vnop_t *) smbfs_vnop_pagein },
{ &vnop_inactive_desc, (vnop_t *) smbfs_vnop_inactive },
{ &vnop_ioctl_desc, (vnop_t *) smbfs_vnop_ioctl },
{ &vnop_link_desc, (vnop_t *) smbfs_vnop_link },
{ &vnop_lookup_desc, (vnop_t *) smbfs_vnop_lookup },
{ &vnop_mkdir_desc, (vnop_t *) smbfs_vnop_mkdir },
{ &vnop_mknod_desc, (vnop_t *) smbfs_vnop_mknod },
{ &vnop_mmap_desc, (vnop_t *) smbfs_vnop_mmap },
{ &vnop_mnomap_desc, (vnop_t *) smbfs_vnop_mnomap },
{ &vnop_open_desc, (vnop_t *) smbfs_vnop_open },
{ &vnop_compound_open_desc, (vnop_t *) smbfs_vnop_compound_open },
{ &vnop_pathconf_desc, (vnop_t *) smbfs_vnop_pathconf },
{ &vnop_pageout_desc, (vnop_t *) smbfs_vnop_pageout },
{ &vnop_read_desc, (vnop_t *) smbfs_vnop_read },
{ &vnop_readdir_desc, (vnop_t *) smbfs_vnop_readdir },
{ &vnop_readlink_desc, (vnop_t *) smbfs_vnop_readlink },
{ &vnop_reclaim_desc, (vnop_t *) smbfs_vnop_reclaim },
{ &vnop_remove_desc, (vnop_t *) smbfs_vnop_remove },
{ &vnop_rename_desc, (vnop_t *) smbfs_vnop_rename },
{ &vnop_rmdir_desc, (vnop_t *) smbfs_vnop_rmdir },
{ &vnop_setattr_desc, (vnop_t *) smbfs_vnop_setattr },
{ &vnop_symlink_desc, (vnop_t *) smbfs_vnop_symlink },
{ &vnop_write_desc, (vnop_t *) smbfs_vnop_write },
{ &vnop_blockmap_desc, (vnop_t *) smbfs_vnop_blockmap },
{ &vnop_strategy_desc, (vnop_t *) smbfs_vnop_strategy },
{ &vnop_searchfs_desc, (vnop_t *) err_searchfs },
{ &vnop_offtoblk_desc, (vnop_t *) smbfs_vnop_offtoblk },
{ &vnop_blktooff_desc, (vnop_t *) smbfs_vnop_blktooff },
{ &vnop_getxattr_desc, (vnop_t *) smbfs_vnop_getxattr },
{ &vnop_setxattr_desc, (vnop_t *) smbfs_vnop_setxattr },
{ &vnop_removexattr_desc, (vnop_t *) smbfs_vnop_removexattr },
{ &vnop_listxattr_desc, (vnop_t *) smbfs_vnop_listxattr },
{ &vnop_monitor_desc, (vnop_t *) smbfs_vnop_monitor},
{ &vnop_getnamedstream_desc, (vnop_t *) smbfs_vnop_getnamedstream },
{ &vnop_makenamedstream_desc, (vnop_t *) smbfs_vnop_makenamedstream },
{ &vnop_removenamedstream_desc, (vnop_t *) smbfs_vnop_removenamedstream },
{ &vnop_access_desc, (vnop_t *) smbfs_vnop_access },
{ &vnop_allocate_desc, (vnop_t *) smbfs_vnop_allocate },
{ NULL, NULL }
};
struct vnodeopv_desc smbfs_vnodeop_opv_desc =
{ &smbfs_vnodeop_p, smbfs_vnodeop_entries };