const SMB_STRUCT_STAT *psbuf);
/****************************************************************************
- Check if an open file handle or pathname is a symlink.
+ Check if an open file handle or smb_fname is a symlink.
****************************************************************************/
static NTSTATUS refuse_symlink(connection_struct *conn,
if (fsp) {
pst = &fsp->fsp_name->st;
} else {
+ pst = &smb_fname->st;
+ }
+
+ if (!VALID_STAT(*pst)) {
int ret = vfs_stat_smb_basename(conn,
smb_fname,
&sbuf);
- if (ret == -1) {
+ if (ret == -1 && errno != ENOENT) {
return map_nt_error_from_unix(errno);
+ } else if (ret == -1) {
+ /* it's not a symlink.. */
+ return NT_STATUS_OK;
}
pst = &sbuf;
}
+
if (S_ISLNK(pst->st_ex_mode)) {
return NT_STATUS_ACCESS_DENIED;
}
uint64_t get_FileIndex(connection_struct *conn, const SMB_STRUCT_STAT *psbuf)
{
uint64_t file_index;
+ if (conn->sconn->aapl_zero_file_id) {
+ return 0;
+ }
if (conn->base_share_dev == psbuf->st_ex_dev) {
return (uint64_t)psbuf->st_ex_ino;
}
return file_index;
}
+
+/********************************************************************
+ Globally (for this connection / multi-channel) disable file-ID
+ calculation. This is required to be global because it serves
+ Macs in AAPL mode, which is globally set.
+********************************************************************/
+void aapl_force_zero_file_id(struct smbd_server_connection *sconn)
+{
+ sconn->aapl_zero_file_id = true;
+}
+
/****************************************************************************
Utility functions for dealing with extended attributes.
****************************************************************************/
char ***pnames,
size_t *pnum_names)
{
+ char smallbuf[1024];
/* Get a list of all xattrs. Max namesize is 64k. */
size_t ea_namelist_size = 1024;
- char *ea_namelist = NULL;
+ char *ea_namelist = smallbuf;
+ char *to_free = NULL;
char *p;
- char **names, **tmp;
+ char **names;
size_t num_names;
ssize_t sizeret = -1;
NTSTATUS status;
}
*pnum_names = 0;
- if (!lp_ea_support(SNUM(conn))) {
- return NT_STATUS_OK;
- }
-
status = refuse_symlink(conn, fsp, smb_fname);
if (!NT_STATUS_IS_OK(status)) {
/*
return NT_STATUS_OK;
}
- /*
- * TALLOC the result early to get the talloc hierarchy right.
- */
-
- names = talloc_array(mem_ctx, char *, 1);
- if (names == NULL) {
- DEBUG(0, ("talloc failed\n"));
- return NT_STATUS_NO_MEMORY;
+ if (fsp && fsp->fh->fd != -1) {
+ sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist,
+ ea_namelist_size);
+ } else {
+ sizeret = SMB_VFS_LISTXATTR(conn,
+ smb_fname->base_name,
+ ea_namelist,
+ ea_namelist_size);
}
- while (ea_namelist_size <= 65536) {
-
- ea_namelist = talloc_realloc(
- names, ea_namelist, char, ea_namelist_size);
+ if ((sizeret == -1) && (errno == ERANGE)) {
+ ea_namelist_size = 65536;
+ ea_namelist = talloc_array(mem_ctx, char, ea_namelist_size);
if (ea_namelist == NULL) {
- DEBUG(0, ("talloc failed\n"));
- TALLOC_FREE(names);
return NT_STATUS_NO_MEMORY;
}
+ to_free = ea_namelist;
if (fsp && fsp->fh->fd != -1) {
sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist,
ea_namelist_size);
} else {
sizeret = SMB_VFS_LISTXATTR(conn,
- smb_fname->base_name,
- ea_namelist,
- ea_namelist_size);
- }
-
- if ((sizeret == -1) && (errno == ERANGE)) {
- ea_namelist_size *= 2;
- }
- else {
- break;
+ smb_fname->base_name,
+ ea_namelist,
+ ea_namelist_size);
}
}
if (sizeret == -1) {
- TALLOC_FREE(names);
- return map_nt_error_from_unix(errno);
+ status = map_nt_error_from_unix(errno);
+ TALLOC_FREE(to_free);
+ return status;
}
- DEBUG(10, ("%s: ea_namelist size = %u\n",
- __func__, (unsigned int)sizeret));
+ DBG_DEBUG("ea_namelist size = %zd\n", sizeret);
if (sizeret == 0) {
- TALLOC_FREE(names);
+ TALLOC_FREE(to_free);
return NT_STATUS_OK;
}
*/
if (ea_namelist[sizeret-1] != '\0') {
- TALLOC_FREE(names);
+ TALLOC_FREE(to_free);
return NT_STATUS_INTERNAL_ERROR;
}
num_names += 1;
}
- tmp = talloc_realloc(mem_ctx, names, char *, num_names);
- if (tmp == NULL) {
+ *pnum_names = num_names;
+
+ if (pnames == NULL) {
+ TALLOC_FREE(to_free);
+ return NT_STATUS_OK;
+ }
+
+ names = talloc_array(mem_ctx, char *, num_names);
+ if (names == NULL) {
DEBUG(0, ("talloc failed\n"));
- TALLOC_FREE(names);
+ TALLOC_FREE(to_free);
return NT_STATUS_NO_MEMORY;
}
- names = tmp;
+ if (ea_namelist == smallbuf) {
+ ea_namelist = talloc_memdup(names, smallbuf, sizeret);
+ if (ea_namelist == NULL) {
+ TALLOC_FREE(names);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ talloc_steal(names, ea_namelist);
+
+ ea_namelist = talloc_realloc(names, ea_namelist, char,
+ sizeret);
+ if (ea_namelist == NULL) {
+ TALLOC_FREE(names);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
num_names = 0;
for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) {
names[num_names++] = p;
}
- if (pnames) {
- *pnames = names;
- } else {
- TALLOC_FREE(names);
- }
- *pnum_names = num_names;
+ *pnames = names;
+
return NT_STATUS_OK;
}
*pea_total_len = 0;
*ea_list = NULL;
+ if (!lp_ea_support(SNUM(conn))) {
+ return NT_STATUS_OK;
+ }
+
if (fsp) {
posix_pathnames =
(fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH);
}
if (num_names == 0) {
- *ea_list = NULL;
return NT_STATUS_OK;
}
int space_remaining,
bool *got_exact_match,
int *_last_entry_off,
- struct ea_list *name_list)
+ struct ea_list *name_list,
+ struct file_id *file_id)
{
const char *p;
const char *mask = NULL;
bool ok;
uint64_t last_entry_off = 0;
NTSTATUS status;
+ enum mangled_names_options mangled_names;
+ bool marshall_with_83_names;
+
+ mangled_names = lp_mangled_names(conn->params);
ZERO_STRUCT(state);
state.conn = conn;
state.info_level = info_level;
- state.check_mangled_names = lp_mangled_names(conn->params);
+ if (mangled_names != MANGLED_NAMES_NO) {
+ state.check_mangled_names = true;
+ }
state.has_wild = dptr_has_wild(dirptr);
state.got_exact_match = false;
*got_exact_match = state.got_exact_match;
+ marshall_with_83_names = (mangled_names == MANGLED_NAMES_YES);
+
status = smbd_marshall_dir_entry(ctx,
conn,
flags2,
info_level,
name_list,
- state.check_mangled_names,
+ marshall_with_83_names,
requires_resume_key,
mode,
fname,
DEBUG(1,("Conversion error: illegal character: %s\n",
smb_fname_str_dbg(smb_fname)));
}
+
+ if (file_id != NULL) {
+ *file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+ }
+
TALLOC_FREE(fname);
TALLOC_FREE(smb_fname);
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
ppdata, base_data, end_data,
space_remaining,
got_exact_match,
- last_entry_off, name_list);
+ last_entry_off, name_list, NULL);
}
/****************************************************************************
{
uint64_t dfree,dsize,bsize,block_size,sectors_per_unit;
data_len = 18;
- df_ret = get_dfree_info(conn, filename, &bsize, &dfree,
- &dsize);
+ df_ret = get_dfree_info(conn, &smb_fname, &bsize,
+ &dfree, &dsize);
if (df_ret == (uint64_t)-1) {
return map_nt_error_from_unix(errno);
}
cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (unsigned int)bsize, (unsigned int)sectors_per_unit,
(unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
+ /*
+ * For large drives, return max values and not modulo.
+ */
+ dsize = MIN(dsize, UINT32_MAX);
+ dfree = MIN(dfree, UINT32_MAX);
+
SIVAL(pdata,l1_idFileSystem,st.st_ex_dev);
SIVAL(pdata,l1_cSectorUnit,sectors_per_unit);
SIVAL(pdata,l1_cUnit,dsize);
{
uint64_t dfree,dsize,bsize,block_size,sectors_per_unit;
data_len = 24;
- df_ret = get_dfree_info(conn, filename, &bsize, &dfree,
- &dsize);
+ df_ret = get_dfree_info(conn, &smb_fname, &bsize,
+ &dfree, &dsize);
if (df_ret == (uint64_t)-1) {
return map_nt_error_from_unix(errno);
}
{
uint64_t dfree,dsize,bsize,block_size,sectors_per_unit;
data_len = 32;
- df_ret = get_dfree_info(conn, filename, &bsize, &dfree,
- &dsize);
+ df_ret = get_dfree_info(conn, &smb_fname, &bsize,
+ &dfree, &dsize);
if (df_ret == (uint64_t)-1) {
return map_nt_error_from_unix(errno);
}
return status;
}
+static NTSTATUS smb_set_fsquota(connection_struct *conn,
+ struct smb_request *req,
+ files_struct *fsp,
+ const DATA_BLOB *qdata)
+{
+ NTSTATUS status;
+ SMB_NTQUOTA_STRUCT quotas;
+
+ ZERO_STRUCT(quotas);
+
+ /* access check */
+ if ((get_current_uid(conn) != 0) || !CAN_WRITE(conn)) {
+ DEBUG(3, ("set_fsquota: access_denied service [%s] user [%s]\n",
+ lp_servicename(talloc_tos(), SNUM(conn)),
+ conn->session_info->unix_info->unix_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!check_fsp_ntquota_handle(conn, req,
+ fsp)) {
+ DEBUG(1, ("set_fsquota: no valid QUOTA HANDLE\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* note: normally there're 48 bytes,
+ * but we didn't use the last 6 bytes for now
+ * --metze
+ */
+ if (qdata->length < 42) {
+ DEBUG(0,("set_fsquota: requires total_data(%u) >= 42 bytes!\n",
+ (unsigned int)qdata->length));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* unknown_1 24 NULL bytes in pdata*/
+
+ /* the soft quotas 8 bytes (uint64_t)*/
+ quotas.softlim = BVAL(qdata->data,24);
+
+ /* the hard quotas 8 bytes (uint64_t)*/
+ quotas.hardlim = BVAL(qdata->data,32);
+
+ /* quota_flags 2 bytes **/
+ quotas.qflags = SVAL(qdata->data,40);
+
+ /* unknown_2 6 NULL bytes follow*/
+
+ /* now set the quotas */
+ if (vfs_set_ntquota(fsp, SMB_USER_FS_QUOTA_TYPE, NULL, "as)!=0) {
+ DEBUG(1, ("vfs_set_ntquota() failed for service [%s]\n",
+ lp_servicename(talloc_tos(), SNUM(conn))));
+ status = map_nt_error_from_unix(errno);
+ } else {
+ status = NT_STATUS_OK;
+ }
+ return status;
+}
+
+NTSTATUS smbd_do_setfsinfo(connection_struct *conn,
+ struct smb_request *req,
+ TALLOC_CTX *mem_ctx,
+ uint16_t info_level,
+ files_struct *fsp,
+ const DATA_BLOB *pdata)
+{
+ switch (info_level) {
+ case SMB_FS_QUOTA_INFORMATION:
+ {
+ return smb_set_fsquota(conn,
+ req,
+ fsp,
+ pdata);
+ }
+
+ default:
+ break;
+ }
+ return NT_STATUS_INVALID_LEVEL;
+}
+
/****************************************************************************
Reply to a TRANS2_QFSINFO (query filesystem info).
****************************************************************************/
case SMB_FS_QUOTA_INFORMATION:
{
+ NTSTATUS status;
+ DATA_BLOB qdata = {
+ .data = (uint8_t *)pdata,
+ .length = total_data
+ };
files_struct *fsp = NULL;
- SMB_NTQUOTA_STRUCT quotas;
-
- ZERO_STRUCT(quotas);
-
- /* access check */
- if ((get_current_uid(conn) != 0) || !CAN_WRITE(conn)) {
- DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
- lp_servicename(talloc_tos(), SNUM(conn)),
- conn->session_info->unix_info->unix_name));
- reply_nterror(req, NT_STATUS_ACCESS_DENIED);
- return;
- }
-
- /* note: normally there're 48 bytes,
- * but we didn't use the last 6 bytes for now
- * --metze
- */
fsp = file_fsp(req, SVAL(params,0));
- if (!check_fsp_ntquota_handle(conn, req,
- fsp)) {
- DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
- reply_nterror(
- req, NT_STATUS_INVALID_HANDLE);
- return;
- }
-
- if (total_data < 42) {
- DEBUG(0,("call_trans2setfsinfo: SET_FS_QUOTA: requires total_data(%d) >= 42 bytes!\n",
- total_data));
- reply_nterror(
- req,
- NT_STATUS_INVALID_PARAMETER);
- return;
- }
-
- /* unknown_1 24 NULL bytes in pdata*/
-
- /* the soft quotas 8 bytes (uint64_t)*/
- quotas.softlim = BVAL(pdata,24);
-
- /* the hard quotas 8 bytes (uint64_t)*/
- quotas.hardlim = BVAL(pdata,32);
-
- /* quota_flags 2 bytes **/
- quotas.qflags = SVAL(pdata,40);
-
- /* unknown_2 6 NULL bytes follow*/
-
- /* now set the quotas */
- if (vfs_set_ntquota(fsp, SMB_USER_FS_QUOTA_TYPE, NULL, "as)!=0) {
- DEBUG(0,("vfs_set_ntquota() failed for service [%s]\n",lp_servicename(talloc_tos(), SNUM(conn))));
- reply_nterror(req, map_nt_error_from_unix(errno));
+ status = smb_set_fsquota(conn,
+ req,
+ fsp,
+ &qdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
return;
}
-
break;
}
default:
}
if (info_level == SMB_QUERY_FILE_UNIX_BASIC ||
info_level == SMB_QUERY_FILE_UNIX_INFO2 ||
- info_level == SMB_QUERY_FILE_UNIX_LINK) {
+ info_level == SMB_QUERY_FILE_UNIX_LINK ||
+ req->posix_pathnames) {
ucf_flags |= UCF_UNIX_NAME_LOOKUP;
}
}
return;
}
- if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (INFO_LEVEL_IS_UNIX(info_level) || req->posix_pathnames) {
/* Always do lstat for UNIX calls. */
if (SMB_VFS_LSTAT(conn, smb_fname_base) != 0) {
DEBUG(3,("call_trans2qfilepathinfo: "
}
}
- if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (INFO_LEVEL_IS_UNIX(info_level) || req->posix_pathnames) {
/* Always do lstat for UNIX calls. */
if (SMB_VFS_LSTAT(conn, smb_fname)) {
DEBUG(3,("call_trans2qfilepathinfo: "
DEBUG(10,("smb_file_rename_information: got name |%s|\n",
newname));
- status = resolve_dfspath_wcard(ctx, conn,
- req->flags2 & FLAGS2_DFS_PATHNAMES,
+ if (req->flags2 & FLAGS2_DFS_PATHNAMES) {
+ status = resolve_dfspath_wcard(ctx, conn,
newname,
UCF_COND_ALLOW_WCARD_LCOMP,
!conn->sconn->using_smb2,
&newname,
&dest_has_wcard);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
}
/* Check the new name has no '/' characters. */