Commit | Line | Data |
---|---|---|
805e021f CE |
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 | /* | |
11 | * Implements: | |
12 | * afs_link | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <afsconfig.h> | |
17 | #include "afs/param.h" | |
18 | ||
19 | ||
20 | #include "afs/sysincludes.h" /* Standard vendor system headers */ | |
21 | #include "afsincludes.h" /* Afs-based standard headers */ | |
22 | #include "afs/afs_stats.h" /* statistics */ | |
23 | #include "afs/afs_cbqueue.h" | |
24 | #include "afs/nfsclient.h" | |
25 | #include "afs/afs_osidnlc.h" | |
26 | ||
27 | extern afs_rwlock_t afs_xcbhash; | |
28 | ||
29 | /* Note that we don't set CDirty here, this is OK because the link | |
30 | * RPC is called synchronously. */ | |
31 | ||
32 | int | |
33 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
34 | afs_link(OSI_VC_DECL(adp), struct vcache *avc, char *aname, | |
35 | afs_ucred_t *acred) | |
36 | #else | |
37 | afs_link(struct vcache *avc, OSI_VC_DECL(adp), char *aname, | |
38 | afs_ucred_t *acred) | |
39 | #endif | |
40 | { | |
41 | struct vrequest *treq = NULL; | |
42 | struct dcache *tdc; | |
43 | afs_int32 code; | |
44 | struct afs_conn *tc; | |
45 | afs_size_t offset, len; | |
46 | struct AFSFetchStatus *OutFidStatus, *OutDirStatus; | |
47 | struct AFSVolSync tsync; | |
48 | struct afs_fakestat_state vfakestate, dfakestate; | |
49 | struct rx_connection *rxconn; | |
50 | XSTATS_DECLS; | |
51 | OSI_VC_CONVERT(adp); | |
52 | ||
53 | AFS_STATCNT(afs_link); | |
54 | afs_Trace3(afs_iclSetp, CM_TRACE_LINK, ICL_TYPE_POINTER, adp, | |
55 | ICL_TYPE_POINTER, avc, ICL_TYPE_STRING, aname); | |
56 | ||
57 | OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); | |
58 | OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); | |
59 | ||
60 | /* create a hard link; new entry is aname in dir adp */ | |
61 | if ((code = afs_CreateReq(&treq, acred))) | |
62 | goto done2; | |
63 | ||
64 | afs_InitFakeStat(&vfakestate); | |
65 | afs_InitFakeStat(&dfakestate); | |
66 | ||
67 | AFS_DISCON_LOCK(); | |
68 | ||
69 | code = afs_EvalFakeStat(&avc, &vfakestate, treq); | |
70 | if (code) | |
71 | goto done; | |
72 | code = afs_EvalFakeStat(&adp, &dfakestate, treq); | |
73 | if (code) | |
74 | goto done; | |
75 | ||
76 | if (avc->f.fid.Cell != adp->f.fid.Cell | |
77 | || avc->f.fid.Fid.Volume != adp->f.fid.Fid.Volume) { | |
78 | code = EXDEV; | |
79 | goto done; | |
80 | } | |
81 | if (strlen(aname) > AFSNAMEMAX) { | |
82 | code = ENAMETOOLONG; | |
83 | goto done; | |
84 | } | |
85 | code = afs_VerifyVCache(adp, treq); | |
86 | if (code) | |
87 | goto done; | |
88 | ||
89 | /** If the volume is read-only, return error without making an RPC to the | |
90 | * fileserver | |
91 | */ | |
92 | if (adp->f.states & CRO) { | |
93 | code = EROFS; | |
94 | goto done; | |
95 | } | |
96 | ||
97 | if (AFS_IS_DISCONNECTED) { | |
98 | code = ENETDOWN; | |
99 | goto done; | |
100 | } | |
101 | ||
102 | tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1); /* test for error below */ | |
103 | ObtainWriteLock(&adp->lock, 145); | |
104 | do { | |
105 | tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn); | |
106 | if (tc) { | |
107 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_LINK); | |
108 | RX_AFS_GUNLOCK(); | |
109 | code = | |
110 | RXAFS_Link(rxconn, (struct AFSFid *)&adp->f.fid.Fid, aname, | |
111 | (struct AFSFid *)&avc->f.fid.Fid, OutFidStatus, | |
112 | OutDirStatus, &tsync); | |
113 | RX_AFS_GLOCK(); | |
114 | XSTATS_END_TIME; | |
115 | ||
116 | } else | |
117 | code = -1; | |
118 | } while (afs_Analyze | |
119 | (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_LINK, | |
120 | SHARED_LOCK, NULL)); | |
121 | ||
122 | if (code) { | |
123 | if (tdc) | |
124 | afs_PutDCache(tdc); | |
125 | if (code < 0) { | |
126 | afs_StaleVCache(adp); | |
127 | } | |
128 | ReleaseWriteLock(&adp->lock); | |
129 | goto done; | |
130 | } | |
131 | if (tdc) | |
132 | ObtainWriteLock(&tdc->lock, 635); | |
133 | if (afs_LocalHero(adp, tdc, OutDirStatus, 1)) { | |
134 | /* we can do it locally */ | |
135 | ObtainWriteLock(&afs_xdcache, 290); | |
136 | code = afs_dir_Create(tdc, aname, &avc->f.fid.Fid); | |
137 | ReleaseWriteLock(&afs_xdcache); | |
138 | if (code) { | |
139 | ZapDCE(tdc); /* surprise error -- invalid value */ | |
140 | DZap(tdc); | |
141 | } | |
142 | } | |
143 | if (tdc) { | |
144 | ReleaseWriteLock(&tdc->lock); | |
145 | afs_PutDCache(tdc); /* drop ref count */ | |
146 | } | |
147 | ReleaseWriteLock(&adp->lock); | |
148 | ObtainWriteLock(&avc->lock, 146); /* correct link count */ | |
149 | ||
150 | /* we could lock both dir and file; since we get the new fid | |
151 | * status back, you'd think we could put it in the cache status | |
152 | * entry at that point. Note that if we don't lock the file over | |
153 | * the rpc call, we have no guarantee that the status info | |
154 | * returned in ustat is the most recent to store in the file's | |
155 | * cache entry */ | |
156 | ||
157 | afs_StaleVCache(avc); /* don't really know new link count */ | |
158 | ReleaseWriteLock(&avc->lock); | |
159 | code = 0; | |
160 | done: | |
161 | code = afs_CheckCode(code, treq, 24); | |
162 | afs_DestroyReq(treq); | |
163 | afs_PutFakeStat(&vfakestate); | |
164 | afs_PutFakeStat(&dfakestate); | |
165 | AFS_DISCON_UNLOCK(); | |
166 | done2: | |
167 | osi_FreeSmallSpace(OutFidStatus); | |
168 | osi_FreeSmallSpace(OutDirStatus); | |
169 | return code; | |
170 | } |