corrections-to-MIT-merge-20040306
[openafs.git] / src / WINNT / afsd / cm_access.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #include <winsock2.h>
16 #endif
17 #include <malloc.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #ifndef DJGPP
21 #include <nb30.h>
22 #endif
23 #include <osi.h>
24
25 #include "afsd.h"
26
27 /* called with scp write-locked, check to see if we have the ACL info we need
28  * and can get it w/o blocking for any locks.
29  *
30  * Never drops the scp lock, but may fail if the access control info comes from
31  * the parent directory, and the parent's scache entry can't be found, or it
32  * can't be locked.  Thus, this must always be called in a while loop to stabilize
33  * things, since we can always lose the race condition getting to the parent vnode.
34  */
35 int cm_HaveAccessRights(struct cm_scache *scp, struct cm_user *up, long rights,
36         long *outRightsp)
37 {
38         cm_scache_t *aclScp;
39         long code;
40         cm_fid_t tfid;
41         int didLock;
42         long trights;
43         
44         didLock = 0;
45         if (scp->fileType == CM_SCACHETYPE_DIRECTORY) {
46                 aclScp = scp;
47                 cm_HoldSCache(scp);
48         }
49         else {
50                 tfid.cell = scp->fid.cell;
51                 tfid.volume = scp->fid.volume;
52                 tfid.vnode = scp->parentVnode;
53                 tfid.unique = scp->parentUnique;
54                 aclScp = cm_FindSCache(&tfid);
55                 if (!aclScp) return 0;
56                 if (aclScp != scp) {
57                         code = lock_TryMutex(&aclScp->mx);
58                         if (code == 0) {
59                                 /* can't get lock safely and easily */
60                                 cm_ReleaseSCache(aclScp);
61                                 return 0;
62                         }
63
64                         /* check that we have a callback, too */
65                         if (!cm_HaveCallback(aclScp)) {
66                                 /* can't use it */
67                                 lock_ReleaseMutex(&aclScp->mx);
68                                 cm_ReleaseSCache(aclScp);
69                                 return 0;
70                         }
71                         didLock = 1;
72                 }
73         }
74
75         lock_AssertMutex(&aclScp->mx);
76         
77         /* now if rights is a subset of the public rights, we're done.
78          * Otherwise, if we an explicit acl entry, we're also in good shape,
79          * and can definitively answer.
80          */
81         if ((~aclScp->anyAccess & rights) == 0) {
82                 *outRightsp = rights;
83         }
84         else {
85                 /* we have to check the specific rights info */
86                 code = cm_FindACLCache(aclScp, up, &trights);
87                 if (code) {
88                         code = 0;
89                         goto done;
90                 }
91                 *outRightsp = trights;
92         }
93
94         /* check mode bits */
95         if (!(scp->unixModeBits & 0400))
96                 *outRightsp &= ~PRSFS_READ;
97         if (!(scp->unixModeBits & 0200))
98                 *outRightsp &= ~PRSFS_WRITE;
99
100         code = 1;
101         /* fall through */
102         
103 done:
104         if (didLock) lock_ReleaseMutex(&aclScp->mx);
105         cm_ReleaseSCache(aclScp);
106         return code;
107 }
108
109 /* called with locked scp; ensures that we have an ACL cache entry for the
110  * user specified by the parameter "up."
111  * In pathological race conditions, this function may return success without
112  * having loaded the entry properly (due to a racing callback revoke), so this
113  * function must also be called in a while loop to make sure that we eventually
114  * succeed.
115  */
116 long cm_GetAccessRights(struct cm_scache *scp, struct cm_user *up,
117         struct cm_req *reqp)
118 {
119         long code;
120         cm_fid_t tfid;
121         cm_scache_t *aclScp;
122
123         /* pretty easy: just force a pass through the fetch status code */
124         
125         osi_Log2(afsd_logp, "GetAccess scp %x user %x", scp, up);
126
127         /* first, start by finding out whether we have a directory or something
128          * else, so we can find what object's ACL we need.
129          */
130         code = cm_SyncOp(scp, NULL, up, reqp, 0, CM_SCACHESYNC_GETSTATUS
131                         | CM_SCACHESYNC_NEEDCALLBACK);
132                         
133         if (code) return code;
134         
135         if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
136                 /* not a dir, use parent dir's acl */
137                 tfid.cell = scp->fid.cell;
138                 tfid.volume = scp->fid.volume;
139                 tfid.vnode = scp->parentVnode;
140                 tfid.unique = scp->parentUnique;
141                 lock_ReleaseMutex(&scp->mx);
142                 code = cm_GetSCache(&tfid, &aclScp, up, reqp);
143                 if (code) {
144                         lock_ObtainMutex(&scp->mx);
145                         return code;
146                 }
147                 
148                 osi_Log1(afsd_logp, "GetAccess parent %x", aclScp);
149                 lock_ObtainMutex(&aclScp->mx);
150                 code = cm_GetCallback(aclScp, up, reqp, 1);
151                 lock_ReleaseMutex(&aclScp->mx);
152                 cm_ReleaseSCache(aclScp);
153                 lock_ObtainMutex(&scp->mx);
154         }
155         else {
156                 code = cm_GetCallback(scp, up, reqp, 1);
157         }
158
159         return code;
160 }