afs: remove vestigial externs for afs_xcbhash
[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
25 #include "afs/sysincludes.h"    /* Standard vendor system headers */
26 #include "afsincludes.h"        /* Afs-based standard headers */
27 #include "afs/afs_stats.h"      /* statistics */
28 #include "afs/afs_cbqueue.h"
29 #include "afs/nfsclient.h"
30 #include "afs/afs_osidnlc.h"
31
32 extern afs_rwlock_t afs_xvcache;
33
34 /* Note: There is the bare bones beginning of symlink hints in the now
35  * defunct afs/afs_lookup.c file. Since they are not in use, making the call
36  * is just a performance hit.
37  */
38
39 static int
40 afs_DisconCreateSymlink(struct vcache *avc, char *aname, 
41                         struct vrequest *areq) {
42     struct dcache *tdc;
43     struct osi_file *tfile;
44     afs_size_t offset, len;
45     int code = 0;
46
47     tdc = afs_GetDCache(avc, 0, areq, &offset, &len, 0);
48     if (!tdc) {
49         /* printf("afs_DisconCreateSymlink: can't get new dcache for symlink.\n"); */
50         return ENETDOWN;
51     }
52
53     len = strlen(aname);
54     avc->f.m.Length = len;
55
56     ObtainWriteLock(&tdc->lock, 720);
57     tfile = afs_CFileOpen(&tdc->f.inode);
58     if (!tfile) {
59         code = EIO;
60         goto done;
61     }
62     afs_AdjustSize(tdc, len);
63     tdc->validPos = len;
64     afs_CFileWrite(tfile, 0, aname, len);
65     afs_CFileClose(tfile);
66  done:
67     ReleaseWriteLock(&tdc->lock);
68     return code;
69 }
70
71 /* don't set CDirty in here because RPC is called synchronously */
72 int 
73 afs_symlink(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, 
74             char *atargetName, struct vcache **tvcp, afs_ucred_t *acred)
75 {
76     afs_uint32 now = 0;
77     struct vrequest *treq = NULL;
78     afs_int32 code = 0;
79     struct afs_conn *tc;
80     struct VenusFid newFid;
81     struct dcache *tdc;
82     afs_size_t offset, len;
83     afs_int32 alen;
84     struct server *hostp = 0;
85     struct vcache *tvc;
86     struct AFSStoreStatus InStatus;
87     struct AFSFetchStatus *OutFidStatus, *OutDirStatus;
88     struct AFSCallBack CallBack;
89     struct AFSVolSync tsync;
90     struct volume *volp = 0;
91     struct afs_fakestat_state fakestate;
92     struct rx_connection *rxconn;
93     XSTATS_DECLS;
94     OSI_VC_CONVERT(adp);
95
96     AFS_STATCNT(afs_symlink);
97     afs_Trace2(afs_iclSetp, CM_TRACE_SYMLINK, ICL_TYPE_POINTER, adp,
98                ICL_TYPE_STRING, aname);
99
100     OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
101     OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
102     memset(&InStatus, 0, sizeof(InStatus));
103
104     if ((code = afs_CreateReq(&treq, acred)))
105         goto done2;
106
107     afs_InitFakeStat(&fakestate);
108
109     AFS_DISCON_LOCK();
110     
111     code = afs_EvalFakeStat(&adp, &fakestate, treq);
112     if (code)
113         goto done;
114
115     if (strlen(aname) > AFSNAMEMAX || strlen(atargetName) > AFSPATHMAX) {
116         code = ENAMETOOLONG;
117         goto done;
118     }
119
120     if (afs_IsDynroot(adp)) {
121         code = afs_DynrootVOPSymlink(adp, acred, aname, atargetName);
122         goto done;
123     }
124     if (afs_IsDynrootMount(adp)) {
125         code = EROFS;
126         goto done;
127     }
128
129     code = afs_VerifyVCache(adp, treq);
130     if (code) {
131         code = afs_CheckCode(code, treq, 30);
132         goto done;
133     }
134
135     /** If the volume is read-only, return error without making an RPC to the
136       * fileserver
137       */
138     if (adp->f.states & CRO) {
139         code = EROFS;
140         goto done;
141     }
142
143     if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
144         code = ENETDOWN;
145         goto done;
146     }
147     
148     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE;
149     InStatus.ClientModTime = osi_Time();
150     alen = strlen(atargetName); /* we want it to include the null */
151     if ( (*atargetName == '#' || *atargetName == '%') && alen > 1 && atargetName[alen-1] == '.') {
152         InStatus.UnixModeBits = 0644;   /* mt pt: null from "." at end */
153         if (alen == 1)
154             alen++;             /* Empty string */
155     } else {
156         InStatus.UnixModeBits = 0755;
157         alen++;                 /* add in the null */
158     }
159     tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1);
160     volp = afs_FindVolume(&adp->f.fid, READ_LOCK);      /*parent is also in same vol */
161     ObtainWriteLock(&adp->lock, 156);
162     if (tdc)
163         ObtainWriteLock(&tdc->lock, 636);
164     /* No further locks: if the SymLink succeeds, it does not matter what happens
165      * to our local copy of the directory. If somebody tampers with it in the meantime,
166      * the copy will be invalidated */
167     if (!AFS_IS_DISCON_RW) {
168         do {
169             tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
170             if (tc) {
171                 hostp = tc->parent->srvr->server;
172                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SYMLINK);
173                 if (adp->f.states & CForeign) {
174                     now = osi_Time();
175                     RX_AFS_GUNLOCK();
176                     code = 
177                         RXAFS_DFSSymlink(rxconn,
178                                          (struct AFSFid *)&adp->f.fid.Fid,
179                                          aname, atargetName, &InStatus,
180                                          (struct AFSFid *)&newFid.Fid,
181                                          OutFidStatus, OutDirStatus,
182                                          &CallBack, &tsync);
183                     RX_AFS_GLOCK();
184                 } else {
185                     RX_AFS_GUNLOCK();
186                     code =
187                         RXAFS_Symlink(rxconn, (struct AFSFid *)&adp->f.fid.Fid,
188                                       aname, atargetName, &InStatus,
189                                       (struct AFSFid *)&newFid.Fid, 
190                                       OutFidStatus, OutDirStatus, &tsync);
191                     RX_AFS_GLOCK();
192                 }
193                 XSTATS_END_TIME;
194             } else
195                 code = -1;
196         } while (afs_Analyze
197                     (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_SYMLINK,
198                      SHARED_LOCK, NULL));
199     } else {
200         newFid.Cell = adp->f.fid.Cell;
201         newFid.Fid.Volume = adp->f.fid.Fid.Volume;
202         afs_GenFakeFid(&newFid, VREG, 0);
203     }
204
205     ObtainWriteLock(&afs_xvcache, 40);
206     if (code) {
207         if (code < 0) {
208             afs_StaleVCache(adp);
209         }
210         ReleaseWriteLock(&adp->lock);
211         ReleaseWriteLock(&afs_xvcache);
212         if (tdc) {
213             ReleaseWriteLock(&tdc->lock);
214             afs_PutDCache(tdc);
215         }
216         goto done;
217     }
218     /* otherwise, we should see if we can make the change to the dir locally */
219     if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) {
220         /* we can do it locally */
221         ObtainWriteLock(&afs_xdcache, 293);
222         /* If the following fails because the name has been created in the meantime, the
223          * directory is out-of-date - the file server knows best! */
224         code = afs_dir_Create(tdc, aname, &newFid.Fid);
225         ReleaseWriteLock(&afs_xdcache);
226         if (code && !AFS_IS_DISCON_RW) {
227             ZapDCE(tdc);        /* surprise error -- use invalid value */
228             DZap(tdc);
229         }
230     }
231     if (tdc) {
232         ReleaseWriteLock(&tdc->lock);
233         afs_PutDCache(tdc);
234     }
235     newFid.Cell = adp->f.fid.Cell;
236     newFid.Fid.Volume = adp->f.fid.Fid.Volume;
237     ReleaseWriteLock(&adp->lock);
238
239     /* now we're done with parent dir, create the link's entry.  Note that
240      * no one can get a pointer to the new cache entry until we release 
241      * the xvcache lock. */
242     tvc = afs_NewVCache(&newFid, hostp);
243     if (!tvc)
244     {
245         code = -2;
246         ReleaseWriteLock(&afs_xvcache);
247         goto done;
248     }
249     ObtainWriteLock(&tvc->lock, 157);
250     ObtainWriteLock(&afs_xcbhash, 500);
251     tvc->f.states |= CStatd;    /* have valid info */
252     tvc->f.states &= ~CBulkFetching;
253
254     if (adp->f.states & CForeign) {
255         tvc->f.states |= CForeign;
256         /* We don't have to worry about losing the callback since we're doing it 
257          * under the afs_xvcache lock actually, afs_NewVCache may drop the 
258          * afs_xvcache lock, if it calls afs_FlushVCache */
259         tvc->cbExpires = CallBack.ExpirationTime + now;
260         afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp);
261     } else {
262         tvc->cbExpires = 0x7fffffff;    /* never expires, they can't change */
263         /* since it never expires, we don't have to queue the callback */
264     }
265     ReleaseWriteLock(&afs_xcbhash);
266
267     if (AFS_IS_DISCON_RW) {
268         attrs->va_mode = InStatus.UnixModeBits;
269         afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VLNK);
270         code = afs_DisconCreateSymlink(tvc, atargetName, treq);
271         if (code) {
272             /* XXX - When this goes wrong, we need to tidy up the changes we made to
273              * the parent, and get rid of the vcache we just created */
274             ReleaseWriteLock(&tvc->lock);
275             ReleaseWriteLock(&afs_xvcache);
276             afs_PutVCache(tvc);
277             goto done;
278         }
279         afs_DisconAddDirty(tvc, VDisconCreate, 0);
280     } else {
281         afs_ProcessFS(tvc, OutFidStatus, treq);
282     }
283
284     if (!tvc->linkData) {
285         tvc->linkData = afs_osi_Alloc(alen);
286         osi_Assert(tvc->linkData != NULL);
287         strncpy(tvc->linkData, atargetName, alen - 1);
288         tvc->linkData[alen - 1] = 0;
289     }
290     ReleaseWriteLock(&tvc->lock);
291     ReleaseWriteLock(&afs_xvcache);
292     if (tvcp)
293         *tvcp = tvc;
294     else
295         afs_PutVCache(tvc);
296     code = 0;
297   done:
298     afs_PutFakeStat(&fakestate);
299     if (volp)
300         afs_PutVolume(volp, READ_LOCK);
301     AFS_DISCON_UNLOCK();
302     code = afs_CheckCode(code, treq, 31);
303     afs_DestroyReq(treq);
304   done2:
305     osi_FreeSmallSpace(OutFidStatus);
306     osi_FreeSmallSpace(OutDirStatus);
307     return code;
308 }
309
310 int
311 afs_MemHandleLink(struct vcache *avc, struct vrequest *areq)
312 {
313     struct dcache *tdc;
314     char *tp, *rbuf;
315     afs_size_t offset, len;
316     afs_int32 tlen, alen;
317     afs_int32 code;
318
319     AFS_STATCNT(afs_MemHandleLink);
320     /* two different formats, one for links protected 644, have a "." at
321      * the end of the file name, which we turn into a null.  Others, 
322      * protected 755, we add a null to the end of */
323     if (!avc->linkData) {
324         void *addr;
325         tdc = afs_GetDCache(avc, (afs_size_t) 0, areq, &offset, &len, 0);
326         if (!tdc) {
327             return EIO;
328         }
329         /* otherwise we have the data loaded, go for it */
330         if (len > 1024) {
331             afs_PutDCache(tdc);
332             return EFAULT;
333         }
334         if (avc->f.m.Mode & 0111)
335             alen = len + 1;     /* regular link */
336         else
337             alen = len;         /* mt point */
338         rbuf = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
339         ObtainReadLock(&tdc->lock);
340         addr = afs_MemCacheOpen(&tdc->f.inode);
341         tlen = len;
342         code = afs_MemReadBlk(addr, 0, rbuf, tlen);
343         afs_MemCacheClose(addr);
344         ReleaseReadLock(&tdc->lock);
345         afs_PutDCache(tdc);
346         rbuf[alen - 1] = 0;
347         alen = strlen(rbuf) + 1;
348         tp = afs_osi_Alloc(alen);       /* make room for terminating null */
349         osi_Assert(tp != NULL);
350         memcpy(tp, rbuf, alen);
351         osi_FreeLargeSpace(rbuf);
352         if (code != len) {
353             afs_osi_Free(tp, alen);
354             return EIO;
355         }
356         avc->linkData = tp;
357     }
358     return 0;
359 }
360
361 int
362 afs_UFSHandleLink(struct vcache *avc, struct vrequest *areq)
363 {
364     struct dcache *tdc;
365     char *tp, *rbuf;
366     void *tfile;
367     afs_size_t offset, len;
368     afs_int32 tlen, alen;
369     afs_int32 code;
370
371     /* two different formats, one for links protected 644, have a "." at the
372      * end of the file name, which we turn into a null.  Others, protected
373      * 755, we add a null to the end of */
374     AFS_STATCNT(afs_UFSHandleLink);
375     if (!avc->linkData) {
376         tdc = afs_GetDCache(avc, (afs_size_t) 0, areq, &offset, &len, 0);
377         afs_Trace3(afs_iclSetp, CM_TRACE_UFSLINK, ICL_TYPE_POINTER, avc,
378                    ICL_TYPE_POINTER, tdc, ICL_TYPE_OFFSET,
379                    ICL_HANDLE_OFFSET(avc->f.m.Length));
380         if (!tdc) {
381             if (AFS_IS_DISCONNECTED)
382                 return ENETDOWN;
383             else
384                 return EIO;
385         }
386         /* otherwise we have the data loaded, go for it */
387         if (len > 1024) {
388             afs_PutDCache(tdc);
389             return EFAULT;
390         }
391         if (avc->f.m.Mode & 0111)
392             alen = len + 1;     /* regular link */
393         else
394             alen = len;         /* mt point */
395         rbuf = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
396         tlen = len;
397         ObtainReadLock(&tdc->lock);
398         tfile = osi_UFSOpen(&tdc->f.inode);
399         if (!tfile) {
400             ReleaseReadLock(&tdc->lock);
401             afs_PutDCache(tdc);
402             osi_FreeLargeSpace(rbuf);
403             return EIO;
404         }
405         code = afs_osi_Read(tfile, -1, rbuf, tlen);
406         osi_UFSClose(tfile);
407         ReleaseReadLock(&tdc->lock);
408         afs_PutDCache(tdc);
409         rbuf[alen - 1] = '\0';
410         alen = strlen(rbuf) + 1;
411         tp = afs_osi_Alloc(alen);       /* make room for terminating null */
412         osi_Assert(tp != NULL);
413         memcpy(tp, rbuf, alen);
414         osi_FreeLargeSpace(rbuf);
415         if (code != tlen) {
416             afs_osi_Free(tp, alen);
417             return EIO;
418         }
419         avc->linkData = tp;
420     }
421     return 0;
422 }
423
424 int
425 afs_readlink(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred)
426 {
427     afs_int32 code;
428     struct vrequest *treq = NULL;
429     char *tp;
430     struct afs_fakestat_state fakestat;
431     OSI_VC_CONVERT(avc);
432
433     AFS_STATCNT(afs_readlink);
434     afs_Trace1(afs_iclSetp, CM_TRACE_READLINK, ICL_TYPE_POINTER, avc);
435     if ((code = afs_CreateReq(&treq, acred)))
436         return code;
437     afs_InitFakeStat(&fakestat);
438
439     AFS_DISCON_LOCK();
440     
441     code = afs_EvalFakeStat(&avc, &fakestat, treq);
442     if (code)
443         goto done;
444     code = afs_VerifyVCache(avc, treq);
445     if (code)
446         goto done;
447     if (vType(avc) != VLNK) {
448         code = EINVAL;
449         goto done;
450     }
451     ObtainWriteLock(&avc->lock, 158);
452     code = afs_HandleLink(avc, treq);
453     /* finally uiomove it to user-land */
454     if (code == 0) {
455         tp = avc->linkData;
456         if (tp)
457             AFS_UIOMOVE(tp, strlen(tp), UIO_READ, auio, code);
458         else {
459             code = EIO;
460         }
461     }
462     ReleaseWriteLock(&avc->lock);
463   done:
464     afs_PutFakeStat(&fakestat);
465     AFS_DISCON_UNLOCK();
466     code = afs_CheckCode(code, treq, 32);
467     afs_DestroyReq(treq);
468     return code;
469 }