Code:
/*
* Windows systems requires special user access to create a reparse symlinks.
* They default to only allow administrator symlink create access. This can
* be changed on the server, but we are going to run into this issue. So if
* we get an access error on the fsctl then we assume this user doesn't have
* create symlink rights and we need to fallback to the old Conrad/Steve
* symlinks. Since the create worked, we know the user has access to the file
* system, they just don't have create symlink rights. We never fallback if
* the server is running darwin.
*/
if ((ioctl_error) && !(UNIX_SERVER(SSTOVC(share)))) {
/*
* <14281932> Could be NetApp server not supporting reparse
* points and returning STATUS_INVALID_DEVICE_REQUEST.
*/
if ((ioctl_error == EACCES) ||
(ioctlp->ret_ntstatus == STATUS_INVALID_DEVICE_REQUEST)) {
smp->sm_flags &= ~MNT_SUPPORTS_REPARSE_SYMLINKS;
error = smbfs_smb_create_windows_symlink(share, create_np,
namep, name_len,
targetp, target_len,
fap, context);
if (!error) {
SMBDEBUG("smbfs_smb_create_windows_symlink failed %d\n", error);
}
}
}
smb-759.40.1/kernel/smbfs/smbfs_smb_2.c
Code:
/*
* This server doesn't support UNIX or reparse point style symbolic links, so
* create a faked up symbolic link, using the Conrad and Steve French method
* for storing and reading symlinks on Window Servers.
*
* NOTE: We should remove creating these in the future, but first we need to see
* if we can get reparse point style symbolic links working.
*
* The calling routine must hold a reference on the share
*/
int
smbfs_smb_create_windows_symlink(struct smb_share *share, struct smbnode *dnp,
const char *name, size_t nmlen,
char *target, size_t targetlen,
struct smbfattr *fap, vfs_context_t context)
{
uint32_t wlen = 0;
uio_t uio = NULL;
char *wdata = NULL;
int error;
SMBFID fid = 0;
uint64_t create_flags = SMB2_CREATE_DO_CREATE | SMB2_CREATE_GET_MAX_ACCESS;
wdata = smbfs_create_windows_symlink_data(target, targetlen, &wlen);
if (!wdata) {
error = ENOMEM;
goto done;
}
uio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
if (uio == NULL) {
error = ENOMEM;
goto done;
}
uio_addiov(uio, CAST_USER_ADDR_T(wdata), wlen);
if (SSTOVC(share)->vc_flags & SMBV_SMB2) {
/* SMB 2/3 - compound Create/Write/Close */
error = smb2fs_smb_cmpd_create_write(share, dnp,
name, nmlen,
NULL, 0,
SMB2_FILE_WRITE_DATA, fap,
create_flags, uio,
NULL, 0,
context);
}
else {
/* SMB 1 */
error = smbfs_smb_create(share, dnp, name, nmlen, SMB2_FILE_WRITE_DATA,
&fid, FILE_CREATE, 0, fap, context);
if (error) {
goto done;
}
error = smb_smb_write(share, fid, uio, 0, context);
(void) smbfs_smb_close(share, fid, context);
}
if (!error) {
/* We just changed the size of the file */
fap->fa_size = wlen;
}
done:
if (uio != NULL) {
uio_free(uio);
}
if (wdata) {
SMB_FREE(wdata, M_TEMP);
}
if (error) {
SMBWARNING("Creating symlink for %s failed! error = %d\n", name, error);
}
return error;
}
Code:
/*
* Create the data required for a faked up symbolic link. This is Conrad and Steve
* French method for storing and reading symlinks on Window Servers.
*/
static void *
smbfs_create_windows_symlink_data(const char *target, size_t targetlen,
uint32_t *rtlen)
{
MD5_CTX md5;
uint32_t state[4];
uint32_t datalen, filelen;
char *wbuf, *wp;
int maxwplen;
uint32_t targlen = (uint32_t)targetlen;
datalen = SMB_SYMHDRLEN + targlen;
filelen = SMB_SYMLEN;
maxwplen = filelen;
SMB_MALLOC(wbuf, void *, filelen, M_TEMP, M_WAITOK);
wp = wbuf;
bcopy(smb_symmagic, wp, SMB_SYMMAGICLEN);
wp += SMB_SYMMAGICLEN;
maxwplen -= SMB_SYMMAGICLEN;
(void)snprintf(wp, maxwplen, "%04d\n", targlen);
wp += SMB_SYMLENLEN;
maxwplen -= SMB_SYMLENLEN;
MD5Init(&md5);
MD5Update(&md5, (unsigned char *)target, targlen);
MD5Final((u_char *)state, &md5);
(void)snprintf(wp, maxwplen, "%08x%08x%08x%08x\n", htobel(state[0]),
htobel(state[1]), htobel(state[2]), htobel(state[3]));
wp += SMB_SYMMD5LEN;
bcopy(target, wp, targlen);
wp += targlen;
if (datalen < filelen) {
*wp++ = '\n';
datalen++;
if (datalen < filelen)
memset((caddr_t)wp, ' ', filelen - datalen);
}
*rtlen = filelen;
return wbuf;
}
smb-759.40.1/kernel/smbfs/smbfs_smb.c