mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-09-22 12:44:11 +08:00
[CIFS] Ensure that cifs multiplex ids do not collide.
Signed-off-by: Steve French (sfrench@us.ibm.com)
This commit is contained in:
parent
a59c658607
commit
1982c344f1
@ -147,6 +147,7 @@ struct TCP_Server_Info {
|
|||||||
/* (returned on Negotiate */
|
/* (returned on Negotiate */
|
||||||
int capabilities; /* allow selective disabling of caps by smb sess */
|
int capabilities; /* allow selective disabling of caps by smb sess */
|
||||||
__u16 timeZone;
|
__u16 timeZone;
|
||||||
|
__u16 CurrentMid; /* multiplex id - rotating counter */
|
||||||
char cryptKey[CIFS_CRYPTO_KEY_SIZE];
|
char cryptKey[CIFS_CRYPTO_KEY_SIZE];
|
||||||
char workstation_RFC1001_name[16]; /* 16th byte is always zero */
|
char workstation_RFC1001_name[16]; /* 16th byte is always zero */
|
||||||
__u32 sequence_number; /* needed for CIFS PDU signature */
|
__u32 sequence_number; /* needed for CIFS PDU signature */
|
||||||
|
@ -1961,18 +1961,17 @@ struct data_blob {
|
|||||||
perhaps add a CreateDevice - to create Pipes and other special .inodes
|
perhaps add a CreateDevice - to create Pipes and other special .inodes
|
||||||
Also note POSIX open flags
|
Also note POSIX open flags
|
||||||
2) Close - to return the last write time to do cache across close more safely
|
2) Close - to return the last write time to do cache across close more safely
|
||||||
3) PosixQFSInfo - to return statfs info
|
3) FindFirst return unique inode number - what about resume key, two
|
||||||
4) FindFirst return unique inode number - what about resume key, two forms short (matches readdir) and full (enough info to cache inodes)
|
forms short (matches readdir) and full (enough info to cache inodes)
|
||||||
5) Mkdir - set mode
|
4) Mkdir - set mode
|
||||||
|
|
||||||
And under consideration:
|
And under consideration:
|
||||||
6) FindClose2 (return nanosecond timestamp ??)
|
5) FindClose2 (return nanosecond timestamp ??)
|
||||||
7) Use nanosecond timestamps throughout all time fields if
|
6) Use nanosecond timestamps throughout all time fields if
|
||||||
corresponding attribute flag is set
|
corresponding attribute flag is set
|
||||||
8) sendfile - handle based copy
|
7) sendfile - handle based copy
|
||||||
9) Direct i/o
|
8) Direct i/o
|
||||||
10) "POSIX ACL" support
|
9) Misc fcntls?
|
||||||
11) Misc fcntls?
|
|
||||||
|
|
||||||
what about fixing 64 bit alignment
|
what about fixing 64 bit alignment
|
||||||
|
|
||||||
@ -2028,7 +2027,7 @@ struct data_blob {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* xsymlink is a symlink format that can be used
|
/* xsymlink is a symlink format (used by MacOS) that can be used
|
||||||
to save symlink info in a regular file when
|
to save symlink info in a regular file when
|
||||||
mounted to operating systems that do not
|
mounted to operating systems that do not
|
||||||
support the cifs Unix extensions or EAs (for xattr
|
support the cifs Unix extensions or EAs (for xattr
|
||||||
|
@ -61,9 +61,9 @@ extern int decode_negTokenInit(unsigned char *security_blob, int length,
|
|||||||
extern int cifs_inet_pton(int, char * source, void *dst);
|
extern int cifs_inet_pton(int, char * source, void *dst);
|
||||||
extern int map_smb_to_linux_error(struct smb_hdr *smb);
|
extern int map_smb_to_linux_error(struct smb_hdr *smb);
|
||||||
extern void header_assemble(struct smb_hdr *, char /* command */ ,
|
extern void header_assemble(struct smb_hdr *, char /* command */ ,
|
||||||
const struct cifsTconInfo *, int /* specifies length
|
const struct cifsTconInfo *, int /* length of
|
||||||
of fixed section (word count) in two byte units */
|
fixed section (word count) in two byte units */);
|
||||||
);
|
extern __u16 GetNextMid(struct TCP_Server_Info *server);
|
||||||
extern struct oplock_q_entry * AllocOplockQEntry(struct inode *, u16,
|
extern struct oplock_q_entry * AllocOplockQEntry(struct inode *, u16,
|
||||||
struct cifsTconInfo *);
|
struct cifsTconInfo *);
|
||||||
extern void DeleteOplockQEntry(struct oplock_q_entry *);
|
extern void DeleteOplockQEntry(struct oplock_q_entry *);
|
||||||
|
@ -330,7 +330,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
|
|||||||
(void **) &pSMB, (void **) &pSMBr);
|
(void **) &pSMB, (void **) &pSMBr);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
pSMB->hdr.Mid = GetNextMid(server);
|
||||||
pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
|
pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
|
||||||
if (extended_security)
|
if (extended_security)
|
||||||
pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
||||||
@ -415,15 +415,14 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
|
|||||||
if(server->secMode & SECMODE_SIGN_REQUIRED)
|
if(server->secMode & SECMODE_SIGN_REQUIRED)
|
||||||
cERROR(1,
|
cERROR(1,
|
||||||
("Server requires /proc/fs/cifs/PacketSigningEnabled"));
|
("Server requires /proc/fs/cifs/PacketSigningEnabled"));
|
||||||
server->secMode &= ~(SECMODE_SIGN_ENABLED |
|
server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
|
||||||
SECMODE_SIGN_REQUIRED);
|
|
||||||
} else if(sign_CIFS_PDUs == 1) {
|
} else if(sign_CIFS_PDUs == 1) {
|
||||||
if((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
|
if((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
|
||||||
server->secMode &= ~(SECMODE_SIGN_ENABLED |
|
server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
|
||||||
SECMODE_SIGN_REQUIRED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cifs_buf_release(pSMB);
|
cifs_buf_release(pSMB);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -519,6 +518,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
|
|||||||
smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */
|
smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */
|
||||||
|
|
||||||
if(ses->server) {
|
if(ses->server) {
|
||||||
|
pSMB->hdr.Mid = GetNextMid(ses->server);
|
||||||
|
|
||||||
if(ses->server->secMode &
|
if(ses->server->secMode &
|
||||||
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
|
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
|
||||||
pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
|
pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
|
||||||
@ -2519,11 +2520,12 @@ findFirstRetry:
|
|||||||
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
|
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
|
||||||
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
|
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
|
||||||
|
|
||||||
if (rc) {/* BB add logic to retry regular search if Unix search
|
if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */
|
||||||
rejected unexpectedly by server */
|
|
||||||
/* BB Add code to handle unsupported level rc */
|
/* BB Add code to handle unsupported level rc */
|
||||||
cFYI(1, ("Error in FindFirst = %d", rc));
|
cFYI(1, ("Error in FindFirst = %d", rc));
|
||||||
cifs_buf_release(pSMB);
|
|
||||||
|
if (pSMB)
|
||||||
|
cifs_buf_release(pSMB);
|
||||||
|
|
||||||
/* BB eventually could optimize out free and realloc of buf */
|
/* BB eventually could optimize out free and realloc of buf */
|
||||||
/* for this case */
|
/* for this case */
|
||||||
@ -2857,7 +2859,10 @@ getDFSRetry:
|
|||||||
(void **) &pSMBr);
|
(void **) &pSMBr);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
/* server pointer checked in called function,
|
||||||
|
but should never be null here anyway */
|
||||||
|
pSMB->hdr.Mid = GetNextMid(ses->server);
|
||||||
pSMB->hdr.Tid = ses->ipc_tid;
|
pSMB->hdr.Tid = ses->ipc_tid;
|
||||||
pSMB->hdr.Uid = ses->Suid;
|
pSMB->hdr.Uid = ses->Suid;
|
||||||
if (ses->capabilities & CAP_STATUS32) {
|
if (ses->capabilities & CAP_STATUS32) {
|
||||||
|
@ -1857,6 +1857,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||||||
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
|
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
|
||||||
NULL /* no tCon exists yet */ , 13 /* wct */ );
|
NULL /* no tCon exists yet */ , 13 /* wct */ );
|
||||||
|
|
||||||
|
smb_buffer->Mid = GetNextMid(ses->server);
|
||||||
pSMB->req_no_secext.AndXCommand = 0xFF;
|
pSMB->req_no_secext.AndXCommand = 0xFF;
|
||||||
pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
|
pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
|
||||||
pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
|
pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
|
||||||
@ -2132,6 +2133,8 @@ CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||||||
/* send SMBsessionSetup here */
|
/* send SMBsessionSetup here */
|
||||||
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
|
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
|
||||||
NULL /* no tCon exists yet */ , 12 /* wct */ );
|
NULL /* no tCon exists yet */ , 12 /* wct */ );
|
||||||
|
|
||||||
|
smb_buffer->Mid = GetNextMid(ses->server);
|
||||||
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
||||||
pSMB->req.AndXCommand = 0xFF;
|
pSMB->req.AndXCommand = 0xFF;
|
||||||
pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
|
pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
|
||||||
@ -2398,6 +2401,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
|
|||||||
/* send SMBsessionSetup here */
|
/* send SMBsessionSetup here */
|
||||||
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
|
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
|
||||||
NULL /* no tCon exists yet */ , 12 /* wct */ );
|
NULL /* no tCon exists yet */ , 12 /* wct */ );
|
||||||
|
|
||||||
|
smb_buffer->Mid = GetNextMid(ses->server);
|
||||||
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
||||||
pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
|
pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
|
||||||
|
|
||||||
@ -2740,6 +2745,8 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||||||
/* send SMBsessionSetup here */
|
/* send SMBsessionSetup here */
|
||||||
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
|
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
|
||||||
NULL /* no tCon exists yet */ , 12 /* wct */ );
|
NULL /* no tCon exists yet */ , 12 /* wct */ );
|
||||||
|
|
||||||
|
smb_buffer->Mid = GetNextMid(ses->server);
|
||||||
pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
|
pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
|
||||||
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
||||||
pSMB->req.AndXCommand = 0xFF;
|
pSMB->req.AndXCommand = 0xFF;
|
||||||
@ -3111,6 +3118,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
|
|||||||
|
|
||||||
header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
|
header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
|
||||||
NULL /*no tid */ , 4 /*wct */ );
|
NULL /*no tid */ , 4 /*wct */ );
|
||||||
|
|
||||||
|
smb_buffer->Mid = GetNextMid(ses->server);
|
||||||
smb_buffer->Uid = ses->Suid;
|
smb_buffer->Uid = ses->Suid;
|
||||||
pSMB = (TCONX_REQ *) smb_buffer;
|
pSMB = (TCONX_REQ *) smb_buffer;
|
||||||
pSMBr = (TCONX_RSP *) smb_buffer_response;
|
pSMBr = (TCONX_RSP *) smb_buffer_response;
|
||||||
|
@ -34,8 +34,6 @@ extern mempool_t *cifs_sm_req_poolp;
|
|||||||
extern mempool_t *cifs_req_poolp;
|
extern mempool_t *cifs_req_poolp;
|
||||||
extern struct task_struct * oplockThread;
|
extern struct task_struct * oplockThread;
|
||||||
|
|
||||||
static __u16 GlobalMid; /* multiplex id - rotating counter */
|
|
||||||
|
|
||||||
/* The xid serves as a useful identifier for each incoming vfs request,
|
/* The xid serves as a useful identifier for each incoming vfs request,
|
||||||
in a similar way to the mid which is useful to track each sent smb,
|
in a similar way to the mid which is useful to track each sent smb,
|
||||||
and CurrentXid can also provide a running counter (although it
|
and CurrentXid can also provide a running counter (although it
|
||||||
@ -51,6 +49,8 @@ _GetXid(void)
|
|||||||
GlobalTotalActiveXid++;
|
GlobalTotalActiveXid++;
|
||||||
if (GlobalTotalActiveXid > GlobalMaxActiveXid)
|
if (GlobalTotalActiveXid > GlobalMaxActiveXid)
|
||||||
GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */
|
GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */
|
||||||
|
if(GlobalTotalActiveXid > 65000)
|
||||||
|
cFYI(1,("warning: more than 65000 requests active"));
|
||||||
xid = GlobalCurrentXid++;
|
xid = GlobalCurrentXid++;
|
||||||
spin_unlock(&GlobalMid_Lock);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
return xid;
|
return xid;
|
||||||
@ -218,6 +218,76 @@ cifs_small_buf_release(void *buf_to_free)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Find a free multiplex id (SMB mid). Otherwise there could be
|
||||||
|
mid collisions which might cause problems, demultiplexing the
|
||||||
|
wrong response to this request. Multiplex ids could collide if
|
||||||
|
one of a series requests takes much longer than the others, or
|
||||||
|
if a very large number of long lived requests (byte range
|
||||||
|
locks or FindNotify requests) are pending. No more than
|
||||||
|
64K-1 requests can be outstanding at one time. If no
|
||||||
|
mids are available, return zero. A future optimization
|
||||||
|
could make the combination of mids and uid the key we use
|
||||||
|
to demultiplex on (rather than mid alone).
|
||||||
|
In addition to the above check, the cifs demultiplex
|
||||||
|
code already used the command code as a secondary
|
||||||
|
check of the frame and if signing is negotiated the
|
||||||
|
response would be discarded if the mid were the same
|
||||||
|
but the signature was wrong. Since the mid is not put in the
|
||||||
|
pending queue until later (when it is about to be dispatched)
|
||||||
|
we do have to limit the number of outstanding requests
|
||||||
|
to somewhat less than 64K-1 although it is hard to imagine
|
||||||
|
so many threads being in the vfs at one time.
|
||||||
|
*/
|
||||||
|
__u16 GetNextMid(struct TCP_Server_Info *server)
|
||||||
|
{
|
||||||
|
__u16 mid = 0;
|
||||||
|
__u16 last_mid;
|
||||||
|
int collision;
|
||||||
|
|
||||||
|
if(server == NULL)
|
||||||
|
return mid;
|
||||||
|
|
||||||
|
spin_lock(&GlobalMid_Lock);
|
||||||
|
last_mid = server->CurrentMid; /* we do not want to loop forever */
|
||||||
|
server->CurrentMid++;
|
||||||
|
/* This nested loop looks more expensive than it is.
|
||||||
|
In practice the list of pending requests is short,
|
||||||
|
fewer than 50, and the mids are likely to be unique
|
||||||
|
on the first pass through the loop unless some request
|
||||||
|
takes longer than the 64 thousand requests before it
|
||||||
|
(and it would also have to have been a request that
|
||||||
|
did not time out) */
|
||||||
|
while(server->CurrentMid != last_mid) {
|
||||||
|
struct list_head *tmp;
|
||||||
|
struct mid_q_entry *mid_entry;
|
||||||
|
|
||||||
|
collision = 0;
|
||||||
|
if(server->CurrentMid == 0)
|
||||||
|
server->CurrentMid++;
|
||||||
|
|
||||||
|
list_for_each(tmp, &server->pending_mid_q) {
|
||||||
|
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
||||||
|
|
||||||
|
if ((mid_entry->mid == server->CurrentMid) &&
|
||||||
|
(mid_entry->midState == MID_REQUEST_SUBMITTED)) {
|
||||||
|
/* This mid is in use, try a different one */
|
||||||
|
collision = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(collision == 0) {
|
||||||
|
mid = server->CurrentMid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
server->CurrentMid++;
|
||||||
|
}
|
||||||
|
spin_unlock(&GlobalMid_Lock);
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NB: MID can not be set if treeCon not passed in, in that
|
||||||
|
case it is responsbility of caller to set the mid */
|
||||||
void
|
void
|
||||||
header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
||||||
const struct cifsTconInfo *treeCon, int word_count
|
const struct cifsTconInfo *treeCon, int word_count
|
||||||
@ -233,7 +303,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
|||||||
(2 * word_count) + sizeof (struct smb_hdr) -
|
(2 * word_count) + sizeof (struct smb_hdr) -
|
||||||
4 /* RFC 1001 length field does not count */ +
|
4 /* RFC 1001 length field does not count */ +
|
||||||
2 /* for bcc field itself */ ;
|
2 /* for bcc field itself */ ;
|
||||||
/* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */
|
/* Note that this is the only network field that has to be converted
|
||||||
|
to big endian and it is done just before we send it */
|
||||||
|
|
||||||
buffer->Protocol[0] = 0xFF;
|
buffer->Protocol[0] = 0xFF;
|
||||||
buffer->Protocol[1] = 'S';
|
buffer->Protocol[1] = 'S';
|
||||||
@ -245,8 +316,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
|||||||
buffer->Pid = cpu_to_le16((__u16)current->tgid);
|
buffer->Pid = cpu_to_le16((__u16)current->tgid);
|
||||||
buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
|
buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
|
||||||
spin_lock(&GlobalMid_Lock);
|
spin_lock(&GlobalMid_Lock);
|
||||||
GlobalMid++;
|
|
||||||
buffer->Mid = GlobalMid;
|
|
||||||
spin_unlock(&GlobalMid_Lock);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
if (treeCon) {
|
if (treeCon) {
|
||||||
buffer->Tid = treeCon->tid;
|
buffer->Tid = treeCon->tid;
|
||||||
@ -256,8 +325,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
|||||||
if (treeCon->ses->capabilities & CAP_STATUS32) {
|
if (treeCon->ses->capabilities & CAP_STATUS32) {
|
||||||
buffer->Flags2 |= SMBFLG2_ERR_STATUS;
|
buffer->Flags2 |= SMBFLG2_ERR_STATUS;
|
||||||
}
|
}
|
||||||
|
/* Uid is not converted */
|
||||||
buffer->Uid = treeCon->ses->Suid; /* always in LE format */
|
buffer->Uid = treeCon->ses->Suid;
|
||||||
|
buffer->Mid = GetNextMid(treeCon->ses->server);
|
||||||
if(multiuser_mount != 0) {
|
if(multiuser_mount != 0) {
|
||||||
/* For the multiuser case, there are few obvious technically */
|
/* For the multiuser case, there are few obvious technically */
|
||||||
/* possible mechanisms to match the local linux user (uid) */
|
/* possible mechanisms to match the local linux user (uid) */
|
||||||
|
Loading…
Reference in New Issue
Block a user