Commit 2843483d authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6: (51 commits)
  [CIFS] log better errors on failed mounts
  [CIFS] Return better error when server requires signing but client forbids
  [CIFS] fix typo
  [CIFS] acl support part 4
  [CIFS] Fix minor problems noticed by scan
  [CIFS] fix bad handling of EAGAIN error on kernel_recvmsg in cifs_demultiplex_thread
  [CIFS] build break
  [CIFS] endian fixes
  [CIFS] endian fixes in new acl code
  [CIFS] Fix some endianness problems in new acl code
  [CIFS] missing #endif from a previous patch
  [CIFS] formatting fixes
  [CIFS] Break up unicode_sessetup string functions
  [CIFS] parse server_GUID in SPNEGO negProt response
  [CIFS]
  [CIFS] Fix endian conversion problem in posix mkdir
  [CIFS] fix build break when lanman not enabled
  [CIFS] remove two sparse warnings
  [CIFS] remove compile warnings when debug disabled
  [CIFS] CIFS ACL support part 3
  ...
parents 26790656 a761ac57
Version 1.51
------------
Fix memory leak in statfs when mounted to very old servers (e.g.
Windows 9x). Add new feature "POSIX open" which allows servers
which support the current POSIX Extensions to provide better semantics
(e.g. delete for open files opened with posix open). Take into
account umask on posix mkdir not just older style mkdir. Add
ability to mount to IPC$ share (which allows CIFS named pipes to be
opened, read and written as if they were files). When 1st tree
connect fails (e.g. due to signing negotiation failure) fix
leak that causes cifsd not to stop and rmmod to fail to cleanup
cifs_request_buffers pool. Fix problem with POSIX Open/Mkdir on
bigendian architectures. Fix possible memory corruption when
EAGAIN returned on kern_recvmsg. Return better error if server
requires packet signing but client has disabled it.
Version 1.50
------------
Fix NTLMv2 signing. NFS server mounted over cifs works (if cifs mount is
......@@ -6,7 +22,10 @@ done with "serverino" mount option). Add support for POSIX Unlink
Samba supports newer POSIX CIFS Protocol Extensions). Add "nounix"
mount option to allow disabling the CIFS Unix Extensions for just
that mount. Fix hang on spinlock in find_writable_file (race when
reopening file after session crash).
reopening file after session crash). Byte range unlock request to
windows server could unlock more bytes (on server copy of file)
than intended if start of unlock request is well before start of
a previous byte range lock that we issued.
Version 1.49
------------
......
......@@ -3,4 +3,4 @@
#
obj-$(CONFIG_CIFS) += cifs.o
cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o ioctl.o sess.o export.o
cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o ioctl.o sess.o export.o cifsacl.o
......@@ -385,10 +385,9 @@ asn1_oid_decode(struct asn1_ctx *ctx,
unsigned long *optr;
size = eoc - ctx->pointer + 1;
*oid = kmalloc(size * sizeof (unsigned long), GFP_ATOMIC);
if (*oid == NULL) {
*oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC);
if (*oid == NULL)
return 0;
}
optr = *oid;
......@@ -581,9 +580,8 @@ decode_negTokenInit(unsigned char *security_blob, int length,
return 0;
} else if ((cls != ASN1_UNI) || (con != ASN1_CON)
|| (tag != ASN1_SEQ)) {
cFYI(1,
("Exit 6 cls = %d con = %d tag = %d end = %p (%d)",
cls, con, tag, end, *end));
cFYI(1, ("cls = %d con = %d tag = %d end = %p (%d)",
cls, con, tag, end, *end));
}
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
......
......@@ -209,13 +209,16 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
i++;
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
length =
sprintf(buf,
"\n%d) %s Uses: %d Type: %s DevInfo: 0x%x "
"Attributes: 0x%x\nPathComponentMax: %d Status: %d",
i, tcon->treeName,
atomic_read(&tcon->useCount),
tcon->nativeFileSystem,
length = sprintf(buf, "\n%d) %s Uses: %d ", i,
tcon->treeName, atomic_read(&tcon->useCount));
buf += length;
if (tcon->nativeFileSystem) {
length = sprintf(buf, "Type: %s ",
tcon->nativeFileSystem);
buf += length;
}
length = sprintf(buf, "DevInfo: 0x%x Attributes: 0x%x"
"\nPathComponentMax: %d Status: %d",
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
le32_to_cpu(tcon->fsAttrInfo.Attributes),
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
......@@ -876,11 +879,16 @@ security_flags_write(struct file *file, const char __user *buffer,
if (count < 3) {
/* single char or single char followed by null */
c = flags_string[0];
if (c == '0' || c == 'n' || c == 'N')
if (c == '0' || c == 'n' || c == 'N') {
extended_security = CIFSSEC_DEF; /* default */
else if (c == '1' || c == 'y' || c == 'Y')
return count;
} else if (c == '1' || c == 'y' || c == 'Y') {
extended_security = CIFSSEC_MAX;
return count;
return count;
} else if (!isdigit(c)) {
cERROR(1, ("invalid flag %c", c));
return -EINVAL;
}
}
/* else we have a number */
......
/*
* fs/cifs/cifsacl.c
*
* Copyright (C) International Business Machines Corp., 2007
* Author(s): Steve French (sfrench@us.ibm.com)
*
* Contains the routines for mapping CIFS/NTFS ACLs
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/fs.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsacl.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#ifdef CONFIG_CIFS_EXPERIMENTAL
static struct cifs_wksid wksidarr[NUM_WK_SIDS] = {
{{1, 0, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0} }, "null user"},
{{1, 1, {0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0} }, "nobody"},
{{1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11), 0, 0, 0, 0} }, "net-users"},
{{1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(18), 0, 0, 0, 0} }, "sys"},
{{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(544), 0, 0, 0} }, "root"},
{{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(545), 0, 0, 0} }, "users"},
{{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(546), 0, 0, 0} }, "guest"}
};
/* security id for everyone */
static const struct cifs_sid sid_everyone =
{1, 1, {0, 0, 0, 0, 0, 0}, {} };
/* group users */
static const struct cifs_sid sid_user =
{1, 2 , {0, 0, 0, 0, 0, 5}, {} };
int match_sid(struct cifs_sid *ctsid)
{
int i, j;
int num_subauth, num_sat, num_saw;
struct cifs_sid *cwsid;
if (!ctsid)
return (-1);
for (i = 0; i < NUM_WK_SIDS; ++i) {
cwsid = &(wksidarr[i].cifssid);
/* compare the revision */
if (ctsid->revision != cwsid->revision)
continue;
/* compare all of the six auth values */
for (j = 0; j < 6; ++j) {
if (ctsid->authority[j] != cwsid->authority[j])
break;
}
if (j < 6)
continue; /* all of the auth values did not match */
/* compare all of the subauth values if any */
num_sat = ctsid->num_subauth;
num_saw = cwsid->num_subauth;
num_subauth = num_sat < num_saw ? num_sat : num_saw;
if (num_subauth) {
for (j = 0; j < num_subauth; ++j) {
if (ctsid->sub_auth[j] != cwsid->sub_auth[j])
break;
}
if (j < num_subauth)
continue; /* all sub_auth values do not match */
}
cFYI(1, ("matching sid: %s\n", wksidarr[i].sidname));
return (0); /* sids compare/match */
}
cFYI(1, ("No matching sid"));
return (-1);
}
/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
the same returns 1, if they do not match returns 0 */
int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid)
{
int i;
int num_subauth, num_sat, num_saw;
if ((!ctsid) || (!cwsid))
return (0);
/* compare the revision */
if (ctsid->revision != cwsid->revision)
return (0);
/* compare all of the six auth values */
for (i = 0; i < 6; ++i) {
if (ctsid->authority[i] != cwsid->authority[i])
return (0);
}
/* compare all of the subauth values if any */
num_sat = ctsid->num_subauth;
num_saw = cwsid->num_subauth;
num_subauth = num_sat < num_saw ? num_sat : num_saw;
if (num_subauth) {
for (i = 0; i < num_subauth; ++i) {
if (ctsid->sub_auth[i] != cwsid->sub_auth[i])
return (0);
}
}
return (1); /* sids compare/match */
}
static void parse_ace(struct cifs_ace *pace, char *end_of_acl)
{
int num_subauth;
/* validate that we do not go past end of acl */
/* XXX this if statement can be removed
if (end_of_acl < (char *)pace + sizeof(struct cifs_ace)) {
cERROR(1, ("ACL too small to parse ACE"));
return;
} */
num_subauth = pace->num_subauth;
if (num_subauth) {
#ifdef CONFIG_CIFS_DEBUG2
int i;
cFYI(1, ("ACE revision %d num_subauth %d",
pace->revision, pace->num_subauth));
for (i = 0; i < num_subauth; ++i) {
cFYI(1, ("ACE sub_auth[%d]: 0x%x", i,
le32_to_cpu(pace->sub_auth[i])));
}
/* BB add length check to make sure that we do not have huge
num auths and therefore go off the end */
cFYI(1, ("RID %d", le32_to_cpu(pace->sub_auth[num_subauth-1])));
#endif
}
return;
}
static void parse_ntace(struct cifs_ntace *pntace, char *end_of_acl)
{
/* validate that we do not go past end of acl */
if (end_of_acl < (char *)pntace + sizeof(struct cifs_ntace)) {
cERROR(1, ("ACL too small to parse NT ACE"));
return;
}
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("NTACE type %d flags 0x%x size %d, access Req 0x%x",
pntace->type, pntace->flags, pntace->size,
pntace->access_req));
#endif
return;
}
static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid)
{
int i;
int num_aces = 0;
int acl_size;
char *acl_base;
struct cifs_ntace **ppntace;
struct cifs_ace **ppace;
/* BB need to add parm so we can store the SID BB */
/* validate that we do not go past end of acl */
if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
cERROR(1, ("ACL too small to parse DACL"));
return;
}
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("DACL revision %d size %d num aces %d",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
le32_to_cpu(pdacl->num_aces)));
#endif
acl_base = (char *)pdacl;
acl_size = sizeof(struct cifs_acl);
num_aces = le32_to_cpu(pdacl->num_aces);
if (num_aces > 0) {
ppntace = kmalloc(num_aces * sizeof(struct cifs_ntace *),
GFP_KERNEL);
ppace = kmalloc(num_aces * sizeof(struct cifs_ace *),
GFP_KERNEL);
/* cifscred->cecount = pdacl->num_aces;
cifscred->ntaces = kmalloc(num_aces *
sizeof(struct cifs_ntace *), GFP_KERNEL);
cifscred->aces = kmalloc(num_aces *
sizeof(struct cifs_ace *), GFP_KERNEL);*/
for (i = 0; i < num_aces; ++i) {
ppntace[i] = (struct cifs_ntace *)
(acl_base + acl_size);
ppace[i] = (struct cifs_ace *) ((char *)ppntace[i] +
sizeof(struct cifs_ntace));
parse_ntace(ppntace[i], end_of_acl);
if (end_of_acl < ((char *)ppace[i] +
(le16_to_cpu(ppntace[i]->size) -
sizeof(struct cifs_ntace)))) {
cERROR(1, ("ACL too small to parse ACE"));
break;
} else
parse_ace(ppace[i], end_of_acl);
/* memcpy((void *)(&(cifscred->ntaces[i])),
(void *)ppntace[i],
sizeof(struct cifs_ntace));
memcpy((void *)(&(cifscred->aces[i])),
(void *)ppace[i],
sizeof(struct cifs_ace)); */
acl_base = (char *)ppntace[i];
acl_size = le16_to_cpu(ppntace[i]->size);
}
kfree(ppace);
kfree(ppntace);
}
return;
}
static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
{
/* BB need to add parm so we can store the SID BB */
/* validate that we do not go past end of acl */
if (end_of_acl < (char *)psid + sizeof(struct cifs_sid)) {
cERROR(1, ("ACL too small to parse SID"));
return -EINVAL;
}
if (psid->num_subauth) {
#ifdef CONFIG_CIFS_DEBUG2
int i;
cFYI(1, ("SID revision %d num_auth %d First subauth 0x%x",
psid->revision, psid->num_subauth, psid->sub_auth[0]));
for (i = 0; i < psid->num_subauth; i++) {
cFYI(1, ("SID sub_auth[%d]: 0x%x ", i,
le32_to_cpu(psid->sub_auth[i])));
}
/* BB add length check to make sure that we do not have huge
num auths and therefore go off the end */
cFYI(1, ("RID 0x%x",
le32_to_cpu(psid->sub_auth[psid->num_subauth-1])));
#endif
}
return 0;
}
/* Convert CIFS ACL to POSIX form */
int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len)
{
int rc;
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
struct cifs_acl *dacl_ptr; /* no need for SACL ptr */
char *end_of_acl = ((char *)pntsd) + acl_len;
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset));
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
dacl_ptr = (struct cifs_acl *)((char *)pntsd +
le32_to_cpu(pntsd->dacloffset));
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("revision %d type 0x%x ooffset 0x%x goffset 0x%x "
"sacloffset 0x%x dacloffset 0x%x",
pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
le32_to_cpu(pntsd->gsidoffset),
le32_to_cpu(pntsd->sacloffset),
le32_to_cpu(pntsd->dacloffset)));
#endif
rc = parse_sid(owner_sid_ptr, end_of_acl);
if (rc)
return rc;
rc = parse_sid(group_sid_ptr, end_of_acl);
if (rc)
return rc;
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr);
/* cifscred->uid = owner_sid_ptr->rid;
cifscred->gid = group_sid_ptr->rid;
memcpy((void *)(&(cifscred->osid)), (void *)owner_sid_ptr,
sizeof (struct cifs_sid));
memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
sizeof (struct cifs_sid)); */
return (0);
}
#endif /* CONFIG_CIFS_EXPERIMENTAL */
/*
* fs/cifs/cifsacl.h
*
* Copyright (c) International Business Machines Corp., 2005
* Copyright (c) International Business Machines Corp., 2007
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
......@@ -22,17 +22,65 @@
#ifndef _CIFSACL_H
#define _CIFSACL_H
#define NUM_AUTHS 6 /* number of authority fields */
#define NUM_SUBAUTHS 5 /* number of sub authority fields */
#define NUM_WK_SIDS 7 /* number of well known sids */
#define SIDNAMELENGTH 20 /* long enough for the ones we care about */
#define READ_BIT 0x4
#define WRITE_BIT 0x2
#define EXEC_BIT 0x1
#define UBITSHIFT 6
#define GBITSHIFT 3
struct cifs_ntsd {
__le16 revision; /* revision level */
__le16 type;
__le32 osidoffset;
__le32 gsidoffset;
__le32 sacloffset;
__le32 dacloffset;
} __attribute__((packed));
struct cifs_sid {
__u8 revision; /* revision level */
__u8 num_subauths;
__u8 num_subauth;
__u8 authority[6];
__le32 sub_auth[5]; /* sub_auth[num_subauth] */ /* BB FIXME endianness BB */
} __attribute__((packed));
struct cifs_acl {
__le16 revision; /* revision level */
__le16 size;
__le32 num_aces;
} __attribute__((packed));
struct cifs_ntace { /* first part of ACE which contains perms */
__u8 type;
__u8 flags;
__le16 size;
__le32 access_req;
} __attribute__((packed));
struct cifs_ace { /* last part of ACE which includes user info */
__u8 revision; /* revision level */
__u8 num_subauth;
__u8 authority[6];
__u32 sub_auth[4];
/* next sub_auth if any ... */
__le32 sub_auth[5];
} __attribute__((packed));
/* everyone */
/* extern const struct cifs_sid sid_everyone;*/
/* group users */
/* extern const struct cifs_sid sid_user;*/
struct cifs_wksid {
struct cifs_sid cifssid;
char sidname[SIDNAMELENGTH];
} __attribute__((packed));
#ifdef CONFIG_CIFS_EXPERIMENTAL
extern int match_sid(struct cifs_sid *);
extern int compare_sids(struct cifs_sid *, struct cifs_sid *);
#endif /* CONFIG_CIFS_EXPERIMENTAL */
#endif /* _CIFSACL_H */
......@@ -345,7 +345,7 @@ static int calc_ntlmv2_hash(struct cifsSesInfo *ses,
user = kmalloc(2 + (len * 2), GFP_KERNEL);
if (user == NULL)
goto calc_exit_2;
len = cifs_strtoUCS(user, ses->userName, len, nls_cp);
len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp);
UniStrupr(user);
hmac_md5_update((char *)user, 2*len, pctxt);
......@@ -356,7 +356,8 @@ static int calc_ntlmv2_hash(struct cifsSesInfo *ses,
domain = kmalloc(2 + (len * 2), GFP_KERNEL);
if (domain == NULL)
goto calc_exit_1;
len = cifs_strtoUCS(domain, ses->domainName, len, nls_cp);
len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len,
nls_cp);
/* the following line was removed since it didn't work well
with lower cased domain name that passed as an option.
Maybe converting the domain name earlier makes sense */
......
......@@ -49,10 +49,6 @@
static struct quotactl_ops cifs_quotactl_ops;
#endif /* QUOTA */
#ifdef CONFIG_CIFS_EXPERIMENTAL
extern struct export_operations cifs_export_ops;
#endif /* EXPERIMENTAL */
int cifsFYI = 0;
int cifsERROR = 1;
int traceSMB = 0;
......@@ -240,9 +236,9 @@ static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd)
cifs_sb = CIFS_SB(inode->i_sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
return 0;
} else /* file mode might have been restricted at mount time
else /* file mode might have been restricted at mount time
on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */
......@@ -312,15 +308,15 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
seq_printf(s, ",domain=%s",
cifs_sb->tcon->ses->domainName);
}
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) ||
!(cifs_sb->tcon->unix_ext))
seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) ||
!(cifs_sb->tcon->unix_ext))
seq_printf(s, ",gid=%d", cifs_sb->mnt_gid);
}
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
seq_printf(s, ",posixpaths");
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) ||
!(cifs_sb->tcon->unix_ext))
seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) ||
!(cifs_sb->tcon->unix_ext))
seq_printf(s, ",gid=%d", cifs_sb->mnt_gid);
seq_printf(s, ",rsize=%d", cifs_sb->rsize);
seq_printf(s, ",wsize=%d", cifs_sb->wsize);
}
......@@ -346,7 +342,7 @@ int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid,
if (pTcon) {
cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
} else {
return -EIO;
rc = -EIO;
}
FreeXid(xid);
......@@ -716,7 +712,7 @@ static int
cifs_init_inodecache(void)
{
cifs_inode_cachep = kmem_cache_create("cifs_inode_cache",
sizeof (struct cifsInodeInfo),
sizeof(struct cifsInodeInfo),
0, (SLAB_RECLAIM_ACCOUNT|
SLAB_MEM_SPREAD),
cifs_init_once);
......@@ -816,8 +812,8 @@ static int
cifs_init_mids(void)
{
cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids",
sizeof (struct mid_q_entry), 0,
SLAB_HWCACHE_ALIGN, NULL);
sizeof(struct mid_q_entry), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (cifs_mid_cachep == NULL)
return -ENOMEM;
......@@ -829,8 +825,8 @@ cifs_init_mids(void)
}
cifs_oplock_cachep = kmem_cache_create("cifs_oplock_structs",
sizeof (struct oplock_q_entry), 0,