linux-26-vlru-cycle-20041012
[openafs.git] / src / afs / VNOPS / afs_vnop_symlink.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 /*
11  * afs_vnop_symlink.c - symlink and readlink vnodeops.
12  *
13  * Implements:
14  * afs_symlink
15  * afs_MemHandleLink
16  * afs_UFSHandleLink
17  * afs_readlink
18  *
19  */
20
21 #include <afsconfig.h>
22 #include "afs/param.h"
23
24 RCSID
25     ("$Header$");
26
27 #include "afs/sysincludes.h"    /* Standard vendor system headers */
28 #include "afsincludes.h"        /* Afs-based standard headers */
29 #include "afs/afs_stats.h"      /* statistics */
30 #include "afs/afs_cbqueue.h"
31 #include "afs/nfsclient.h"
32 #include "afs/afs_osidnlc.h"
33
34 extern afs_rwlock_t afs_xvcache;
35 extern afs_rwlock_t afs_xcbhash;
36
37 /* Note: There is the bare bones beginning of symlink hints in the now
38  * defunct afs/afs_lookup.c file. Since they are not in use, making the call
39  * is just a performance hit.
40  */
41
42
43 /* don't set CDirty in here because RPC is called synchronously */
44 int afs_symlink
45 #ifdef  AFS_OSF_ENV
46   (ndp, attrs, atargetName)
47      struct nameidata *ndp;
48      struct vattr *attrs;
49      register char *atargetName;
50 {
51     register struct vcache *adp = VTOAFS(ndp->ni_dvp);
52     char *aname = ndp->ni_dent.d_name;
53     struct ucred *acred = ndp->ni_cred;
54 #else                           /* AFS_OSF_ENV */
55   (OSI_VC_ARG(adp), aname, attrs, atargetName, acred)
56      OSI_VC_DECL(adp);
57      char *atargetName;
58      char *aname;
59      struct vattr *attrs;
60      struct AFS_UCRED *acred;
61 {
62 #endif
63     afs_uint32 now = 0;
64     struct vrequest treq;
65     afs_int32 code;
66     struct conn *tc;
67     struct VenusFid newFid;
68     struct dcache *tdc;
69     afs_size_t offset, len;
70     afs_int32 alen;
71     struct server *hostp = 0;
72     struct vcache *tvc;
73     struct AFSStoreStatus InStatus;
74     struct AFSFetchStatus OutFidStatus, OutDirStatus;
75     struct AFSCallBack CallBack;
76     struct AFSVolSync tsync;
77     struct volume *volp = 0;
78     struct afs_fakestat_state fakestate;
79     XSTATS_DECLS;
80     OSI_VC_CONVERT(adp);
81
82     AFS_STATCNT(afs_symlink);
83     afs_Trace2(afs_iclSetp, CM_TRACE_SYMLINK, ICL_TYPE_POINTER, adp,
84                ICL_TYPE_STRING, aname);
85
86     if ((code = afs_InitReq(&treq, acred)))
87         goto done2;
88
89     afs_InitFakeStat(&fakestate);
90     code = afs_EvalFakeStat(&adp, &fakestate, &treq);
91     if (code)
92         goto done;
93
94     if (strlen(aname) > AFSNAMEMAX || strlen(atargetName) > AFSPATHMAX) {
95         code = ENAMETOOLONG;
96         goto done;
97     }
98
99     if (afs_IsDynroot(adp)) {
100         code = afs_DynrootVOPSymlink(adp, acred, aname, atargetName);
101         goto done;
102     }
103
104     code = afs_VerifyVCache(adp, &treq);
105     if (code) {
106         code = afs_CheckCode(code, &treq, 30);
107         goto done;
108     }
109
110     /** If the volume is read-only, return error without making an RPC to the
111       * fileserver
112       */
113     if (adp->states & CRO) {
114         code = EROFS;
115         goto done;
116     }
117
118     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE;
119     InStatus.ClientModTime = osi_Time();
120     alen = strlen(atargetName); /* we want it to include the null */
121     if (*atargetName == '#' || *atargetName == '%') {
122         InStatus.UnixModeBits = 0644;   /* mt pt: null from "." at end */
123         if (alen == 1)
124             alen++;             /* Empty string */
125     } else {
126         InStatus.UnixModeBits = 0755;
127         alen++;                 /* add in the null */
128     }
129     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);
130     volp = afs_FindVolume(&adp->fid, READ_LOCK);        /*parent is also in same vol */
131     ObtainWriteLock(&adp->lock, 156);
132     if (tdc)
133         ObtainWriteLock(&tdc->lock, 636);
134     ObtainSharedLock(&afs_xvcache, 17); /* prevent others from creating this entry */
135     /* XXX Pay attention to afs_xvcache around the whole thing!! XXX */
136     do {
137         tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK);
138         if (tc) {
139             hostp = tc->srvr->server;
140             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SYMLINK);
141             if (adp->states & CForeign) {
142                 now = osi_Time();
143                 RX_AFS_GUNLOCK();
144                 code =
145                     RXAFS_DFSSymlink(tc->id, (struct AFSFid *)&adp->fid.Fid,
146                                      aname, atargetName, &InStatus,
147                                      (struct AFSFid *)&newFid.Fid,
148                                      &OutFidStatus, &OutDirStatus, &CallBack,
149                                      &tsync);
150                 RX_AFS_GLOCK();
151             } else {
152                 RX_AFS_GUNLOCK();
153                 code =
154                     RXAFS_Symlink(tc->id, (struct AFSFid *)&adp->fid.Fid,
155                                   aname, atargetName, &InStatus,
156                                   (struct AFSFid *)&newFid.Fid, &OutFidStatus,
157                                   &OutDirStatus, &tsync);
158                 RX_AFS_GLOCK();
159             }
160             XSTATS_END_TIME;
161         } else
162             code = -1;
163     } while (afs_Analyze
164              (tc, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_SYMLINK,
165               SHARED_LOCK, NULL));
166
167     UpgradeSToWLock(&afs_xvcache, 40);
168     if (code) {
169         if (code < 0) {
170             ObtainWriteLock(&afs_xcbhash, 499);
171             afs_DequeueCallback(adp);
172             adp->states &= ~CStatd;
173             ReleaseWriteLock(&afs_xcbhash);
174             osi_dnlc_purgedp(adp);
175         }
176         ReleaseWriteLock(&adp->lock);
177         ReleaseWriteLock(&afs_xvcache);
178         if (tdc) {
179             ReleaseWriteLock(&tdc->lock);
180             afs_PutDCache(tdc);
181         }
182         goto done;
183     }
184     /* otherwise, we should see if we can make the change to the dir locally */
185     if (afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
186         /* we can do it locally */
187         code = afs_dir_Create(&tdc->f.inode, aname, &newFid.Fid);
188         if (code) {
189             ZapDCE(tdc);        /* surprise error -- use invalid value */
190             DZap(&tdc->f.inode);
191         }
192     }
193     if (tdc) {
194         ReleaseWriteLock(&tdc->lock);
195         afs_PutDCache(tdc);
196     }
197     newFid.Cell = adp->fid.Cell;
198     newFid.Fid.Volume = adp->fid.Fid.Volume;
199     ReleaseWriteLock(&adp->lock);
200
201     /* now we're done with parent dir, create the link's entry.  Note that
202      * no one can get a pointer to the new cache entry until we release 
203      * the xvcache lock. */
204     tvc = afs_NewVCache(&newFid, hostp);
205     ObtainWriteLock(&tvc->lock, 157);
206     ObtainWriteLock(&afs_xcbhash, 500);
207     tvc->states |= CStatd;      /* have valid info */
208     tvc->states &= ~CBulkFetching;
209
210     if (adp->states & CForeign) {
211         tvc->states |= CForeign;
212         /* We don't have to worry about losing the callback since we're doing it 
213          * under the afs_xvcache lock actually, afs_NewVCache may drop the 
214          * afs_xvcache lock, if it calls afs_FlushVCache */
215         tvc->cbExpires = CallBack.ExpirationTime + now;
216         afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp);
217     } else {
218         tvc->cbExpires = 0x7fffffff;    /* never expires, they can't change */
219         /* since it never expires, we don't have to queue the callback */
220     }
221     ReleaseWriteLock(&afs_xcbhash);
222     afs_ProcessFS(tvc, &OutFidStatus, &treq);
223
224     if (!tvc->linkData) {
225         tvc->linkData = (char *)afs_osi_Alloc(alen);
226         strncpy(tvc->linkData, atargetName, alen - 1);
227         tvc->linkData[alen - 1] = 0;
228     }
229     ReleaseWriteLock(&tvc->lock);
230     ReleaseWriteLock(&afs_xvcache);
231     afs_PutVCache(tvc);
232     code = 0;
233   done:
234     afs_PutFakeStat(&fakestate);
235     if (volp)
236         afs_PutVolume(volp, READ_LOCK);
237     code = afs_CheckCode(code, &treq, 31);
238   done2:
239 #ifdef  AFS_OSF_ENV
240     AFS_RELE(ndp->ni_dvp);
241 #endif /* AFS_OSF_ENV */
242     return code;
243 }
244
245 int
246 afs_MemHandleLink(register struct vcache *avc, struct vrequest *areq)
247 {
248     register struct dcache *tdc;
249     register char *tp, *rbuf;
250     afs_size_t offset, len;
251     afs_int32 tlen, alen;
252     register afs_int32 code;
253
254     AFS_STATCNT(afs_MemHandleLink);
255     /* two different formats, one for links protected 644, have a "." at
256      * the end of the file name, which we turn into a null.  Others, 
257      * protected 755, we add a null to the end of */
258     if (!avc->linkData) {
259         void *addr;
260         tdc = afs_GetDCache(avc, (afs_size_t) 0, areq, &offset, &len, 0);
261         if (!tdc) {
262             return EIO;
263         }
264         /* otherwise we have the data loaded, go for it */
265         if (len > 1024) {
266             afs_PutDCache(tdc);
267             return EFAULT;
268         }
269         if (avc->m.Mode & 0111)
270             alen = len + 1;     /* regular link */
271         else
272             alen = len;         /* mt point */
273         rbuf = (char *)osi_AllocLargeSpace(AFS_LRALLOCSIZ);
274         ObtainReadLock(&tdc->lock);
275         addr = afs_MemCacheOpen(tdc->f.inode);
276         tlen = len;
277         code = afs_MemReadBlk(addr, 0, rbuf, tlen);
278         afs_MemCacheClose(addr);
279         ReleaseReadLock(&tdc->lock);
280         afs_PutDCache(tdc);
281         rbuf[alen - 1] = 0;
282         alen = strlen(rbuf) + 1;
283         tp = afs_osi_Alloc(alen);       /* make room for terminating null */
284         memcpy(tp, rbuf, alen);
285         osi_FreeLargeSpace(rbuf);
286         if (code != len) {
287             afs_osi_Free(tp, alen);
288             return EIO;
289         }
290         avc->linkData = tp;
291     }
292     return 0;
293 }
294
295 int
296 afs_UFSHandleLink(register struct vcache *avc, struct vrequest *areq)
297 {
298     register struct dcache *tdc;
299     register char *tp, *rbuf;
300     void *tfile;
301     afs_size_t offset, len;
302     afs_int32 tlen, alen;
303     register afs_int32 code;
304
305     /* two different formats, one for links protected 644, have a "." at the
306      * end of the file name, which we turn into a null.  Others, protected
307      * 755, we add a null to the end of */
308     AFS_STATCNT(afs_UFSHandleLink);
309     if (!avc->linkData) {
310         tdc = afs_GetDCache(avc, (afs_size_t) 0, areq, &offset, &len, 0);
311         afs_Trace3(afs_iclSetp, CM_TRACE_UFSLINK, ICL_TYPE_POINTER, avc,
312                    ICL_TYPE_POINTER, tdc, ICL_TYPE_OFFSET,
313                    ICL_HANDLE_OFFSET(avc->m.Length));
314         if (!tdc) {
315             return EIO;
316         }
317         /* otherwise we have the data loaded, go for it */
318         if (len > 1024) {
319             afs_PutDCache(tdc);
320             return EFAULT;
321         }
322         if (avc->m.Mode & 0111)
323             alen = len + 1;     /* regular link */
324         else
325             alen = len;         /* mt point */
326         rbuf = (char *)osi_AllocLargeSpace(AFS_LRALLOCSIZ);
327         tlen = len;
328         ObtainReadLock(&tdc->lock);
329         tfile = osi_UFSOpen(tdc->f.inode);
330         code = afs_osi_Read(tfile, -1, rbuf, tlen);
331         osi_UFSClose(tfile);
332         ReleaseReadLock(&tdc->lock);
333         afs_PutDCache(tdc);
334         rbuf[alen - 1] = '\0';
335         alen = strlen(rbuf) + 1;
336         tp = afs_osi_Alloc(alen);       /* make room for terminating null */
337         memcpy(tp, rbuf, alen);
338         osi_FreeLargeSpace(rbuf);
339         if (code != tlen) {
340             afs_osi_Free(tp, alen);
341             return EIO;
342         }
343         avc->linkData = tp;
344     }
345     return 0;
346 }
347
348 int
349 afs_readlink(OSI_VC_ARG(avc), auio, acred)
350      OSI_VC_DECL(avc);
351      struct uio *auio;
352      struct AFS_UCRED *acred;
353 {
354     register afs_int32 code;
355     struct vrequest treq;
356     register char *tp;
357     struct afs_fakestat_state fakestat;
358     OSI_VC_CONVERT(avc);
359
360     AFS_STATCNT(afs_readlink);
361     afs_Trace1(afs_iclSetp, CM_TRACE_READLINK, ICL_TYPE_POINTER, avc);
362     if ((code = afs_InitReq(&treq, acred)))
363         return code;
364     afs_InitFakeStat(&fakestat);
365     code = afs_EvalFakeStat(&avc, &fakestat, &treq);
366     if (code)
367         goto done;
368     code = afs_VerifyVCache(avc, &treq);
369     if (code)
370         goto done;
371     if (vType(avc) != VLNK) {
372         code = EINVAL;
373         goto done;
374     }
375     ObtainWriteLock(&avc->lock, 158);
376     code = afs_HandleLink(avc, &treq);
377     /* finally uiomove it to user-land */
378     if (code == 0) {
379         tp = avc->linkData;
380         if (tp)
381             AFS_UIOMOVE(tp, strlen(tp), UIO_READ, auio, code);
382         else {
383             code = EIO;
384         }
385     }
386     ReleaseWriteLock(&avc->lock);
387   done:
388     afs_PutFakeStat(&fakestat);
389     code = afs_CheckCode(code, &treq, 32);
390     return code;
391 }