| 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_create |
| 13 | * afs_LocalHero |
| 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 | #include "afs/unified_afs.h" |
| 27 | |
| 28 | /* question: does afs_create need to set CDirty in the adp or the avc? |
| 29 | * I think we can get away without it, but I'm not sure. Note that |
| 30 | * afs_setattr is called in here for truncation. |
| 31 | */ |
| 32 | #ifdef AFS_SGI64_ENV |
| 33 | int |
| 34 | afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, int flags, |
| 35 | int amode, struct vcache **avcp, afs_ucred_t *acred) |
| 36 | #else /* AFS_SGI64_ENV */ |
| 37 | int |
| 38 | afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, |
| 39 | enum vcexcl aexcl, int amode, struct vcache **avcp, |
| 40 | afs_ucred_t *acred) |
| 41 | #endif /* AFS_SGI64_ENV */ |
| 42 | { |
| 43 | afs_int32 origCBs, origZaps, finalZaps; |
| 44 | struct vrequest *treq = NULL; |
| 45 | afs_int32 code; |
| 46 | struct afs_conn *tc; |
| 47 | struct VenusFid newFid; |
| 48 | struct AFSStoreStatus InStatus; |
| 49 | struct AFSFetchStatus *OutFidStatus, *OutDirStatus; |
| 50 | struct AFSVolSync tsync; |
| 51 | struct AFSCallBack CallBack; |
| 52 | afs_int32 now; |
| 53 | struct dcache *tdc; |
| 54 | afs_size_t offset, len; |
| 55 | struct server *hostp = 0; |
| 56 | struct vcache *tvc; |
| 57 | struct volume *volp = 0; |
| 58 | struct afs_fakestat_state fakestate; |
| 59 | struct rx_connection *rxconn; |
| 60 | XSTATS_DECLS; |
| 61 | OSI_VC_CONVERT(adp); |
| 62 | |
| 63 | AFS_STATCNT(afs_create); |
| 64 | |
| 65 | OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); |
| 66 | OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); |
| 67 | memset(&InStatus, 0, sizeof(InStatus)); |
| 68 | |
| 69 | if ((code = afs_CreateReq(&treq, acred))) |
| 70 | goto done2; |
| 71 | |
| 72 | afs_Trace3(afs_iclSetp, CM_TRACE_CREATE, ICL_TYPE_POINTER, adp, |
| 73 | ICL_TYPE_STRING, aname, ICL_TYPE_INT32, amode); |
| 74 | |
| 75 | afs_InitFakeStat(&fakestate); |
| 76 | |
| 77 | #ifdef AFS_SGI65_ENV |
| 78 | /* If avcp is passed not null, it's the old reference to this file. |
| 79 | * We can use this to avoid create races. For now, just decrement |
| 80 | * the reference count on it. |
| 81 | */ |
| 82 | if (*avcp) { |
| 83 | AFS_RELE(AFSTOV(*avcp)); |
| 84 | *avcp = NULL; |
| 85 | } |
| 86 | #endif |
| 87 | |
| 88 | if (strlen(aname) > AFSNAMEMAX) { |
| 89 | code = ENAMETOOLONG; |
| 90 | goto done3; |
| 91 | } |
| 92 | |
| 93 | if (!afs_ENameOK(aname)) { |
| 94 | code = EINVAL; |
| 95 | goto done3; |
| 96 | } |
| 97 | switch (attrs->va_type) { |
| 98 | case VBLK: |
| 99 | case VCHR: |
| 100 | #if !defined(AFS_SUN5_ENV) |
| 101 | case VSOCK: |
| 102 | #endif |
| 103 | case VFIFO: |
| 104 | /* We don't support special devices or FIFOs */ |
| 105 | code = EINVAL; |
| 106 | goto done3; |
| 107 | default: |
| 108 | ; |
| 109 | } |
| 110 | AFS_DISCON_LOCK(); |
| 111 | |
| 112 | code = afs_EvalFakeStat(&adp, &fakestate, treq); |
| 113 | if (code) |
| 114 | goto done; |
| 115 | tagain: |
| 116 | code = afs_VerifyVCache(adp, treq); |
| 117 | if (code) |
| 118 | goto done; |
| 119 | |
| 120 | /** If the volume is read-only, return error without making an RPC to the |
| 121 | * fileserver |
| 122 | */ |
| 123 | if (adp->f.states & CRO) { |
| 124 | code = EROFS; |
| 125 | goto done; |
| 126 | } |
| 127 | |
| 128 | if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { |
| 129 | code = ENETDOWN; |
| 130 | goto done; |
| 131 | } |
| 132 | |
| 133 | tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1); |
| 134 | |
| 135 | /** Prevent multiple fetchStatus calls to fileserver when afs_GetDCache() |
| 136 | * returns NULL for an error condition |
| 137 | */ |
| 138 | if (!tdc) { |
| 139 | code = EIO; |
| 140 | goto done; |
| 141 | } |
| 142 | |
| 143 | ObtainWriteLock(&adp->lock, 135); |
| 144 | if (tdc) |
| 145 | ObtainSharedLock(&tdc->lock, 630); |
| 146 | |
| 147 | /* |
| 148 | * Make sure that the data in the cache is current. We may have |
| 149 | * received a callback while we were waiting for the write lock. |
| 150 | */ |
| 151 | if (!(adp->f.states & CStatd) |
| 152 | || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) { |
| 153 | ReleaseWriteLock(&adp->lock); |
| 154 | if (tdc) { |
| 155 | ReleaseSharedLock(&tdc->lock); |
| 156 | afs_PutDCache(tdc); |
| 157 | } |
| 158 | goto tagain; |
| 159 | } |
| 160 | if (tdc) { |
| 161 | /* see if file already exists. If it does, we only set |
| 162 | * the size attributes (to handle O_TRUNC) */ |
| 163 | code = afs_dir_Lookup(tdc, aname, &newFid.Fid); /* use dnlc first xxx */ |
| 164 | if (code == 0) { |
| 165 | ReleaseSharedLock(&tdc->lock); |
| 166 | afs_PutDCache(tdc); |
| 167 | ReleaseWriteLock(&adp->lock); |
| 168 | #ifdef AFS_SGI64_ENV |
| 169 | if (flags & VEXCL) { |
| 170 | #else |
| 171 | if (aexcl != NONEXCL) { |
| 172 | #endif |
| 173 | code = EEXIST; /* file exists in excl mode open */ |
| 174 | goto done; |
| 175 | } |
| 176 | /* found the file, so use it */ |
| 177 | newFid.Cell = adp->f.fid.Cell; |
| 178 | newFid.Fid.Volume = adp->f.fid.Fid.Volume; |
| 179 | tvc = NULL; |
| 180 | if (newFid.Fid.Unique == 0) { |
| 181 | tvc = afs_LookupVCache(&newFid, treq, NULL, adp, aname); |
| 182 | } |
| 183 | if (!tvc) /* lookup failed or wasn't called */ |
| 184 | tvc = afs_GetVCache(&newFid, treq, NULL, NULL); |
| 185 | |
| 186 | if (tvc) { |
| 187 | /* if the thing exists, we need the right access to open it. |
| 188 | * we must check that here, since no other checks are |
| 189 | * made by the open system call */ |
| 190 | len = attrs->va_size; /* only do the truncate */ |
| 191 | /* |
| 192 | * We used to check always for READ access before; the |
| 193 | * problem is that we will fail if the existing file |
| 194 | * has mode -w-w-w, which is wrong. |
| 195 | */ |
| 196 | if ((amode & VREAD) |
| 197 | && !afs_AccessOK(tvc, PRSFS_READ, treq, CHECK_MODE_BITS)) { |
| 198 | afs_PutVCache(tvc); |
| 199 | code = EACCES; |
| 200 | goto done; |
| 201 | } |
| 202 | #if defined(AFS_DARWIN80_ENV) |
| 203 | if ((amode & VWRITE) || VATTR_IS_ACTIVE(attrs, va_data_size)) |
| 204 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) |
| 205 | if ((amode & VWRITE) || (attrs->va_mask & AT_SIZE)) |
| 206 | #else |
| 207 | if ((amode & VWRITE) || len != 0xffffffff) |
| 208 | #endif |
| 209 | { |
| 210 | /* needed for write access check */ |
| 211 | tvc->f.parent.vnode = adp->f.fid.Fid.Vnode; |
| 212 | tvc->f.parent.unique = adp->f.fid.Fid.Unique; |
| 213 | /* need write mode for these guys */ |
| 214 | if (!afs_AccessOK |
| 215 | (tvc, PRSFS_WRITE, treq, CHECK_MODE_BITS)) { |
| 216 | afs_PutVCache(tvc); |
| 217 | code = EACCES; |
| 218 | goto done; |
| 219 | } |
| 220 | } |
| 221 | #if defined(AFS_DARWIN80_ENV) |
| 222 | if (VATTR_IS_ACTIVE(attrs, va_data_size)) |
| 223 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) |
| 224 | if (attrs->va_mask & AT_SIZE) |
| 225 | #else |
| 226 | if (len != 0xffffffff) |
| 227 | #endif |
| 228 | { |
| 229 | if (vType(tvc) != VREG) { |
| 230 | afs_PutVCache(tvc); |
| 231 | code = EISDIR; |
| 232 | goto done; |
| 233 | } |
| 234 | /* do a truncate */ |
| 235 | #if defined(AFS_DARWIN80_ENV) |
| 236 | VATTR_INIT(attrs); |
| 237 | VATTR_SET_SUPPORTED(attrs, va_data_size); |
| 238 | VATTR_SET_ACTIVE(attrs, va_data_size); |
| 239 | #elif defined(UKERNEL) |
| 240 | attrs->va_mask = ATTR_SIZE; |
| 241 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) |
| 242 | attrs->va_mask = AT_SIZE; |
| 243 | #else |
| 244 | VATTR_NULL(attrs); |
| 245 | #endif |
| 246 | attrs->va_size = len; |
| 247 | ObtainWriteLock(&tvc->lock, 136); |
| 248 | tvc->f.states |= CCreating; |
| 249 | ReleaseWriteLock(&tvc->lock); |
| 250 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) |
| 251 | #if defined(AFS_SGI64_ENV) |
| 252 | code = |
| 253 | afs_setattr(VNODE_TO_FIRST_BHV((vnode_t *) tvc), |
| 254 | attrs, 0, acred); |
| 255 | #else |
| 256 | code = afs_setattr(tvc, attrs, 0, acred); |
| 257 | #endif /* AFS_SGI64_ENV */ |
| 258 | #else /* SUN5 || SGI */ |
| 259 | code = afs_setattr(tvc, attrs, acred); |
| 260 | #endif /* SUN5 || SGI */ |
| 261 | ObtainWriteLock(&tvc->lock, 137); |
| 262 | tvc->f.states &= ~CCreating; |
| 263 | ReleaseWriteLock(&tvc->lock); |
| 264 | if (code) { |
| 265 | afs_PutVCache(tvc); |
| 266 | goto done; |
| 267 | } |
| 268 | } |
| 269 | *avcp = tvc; |
| 270 | |
| 271 | } else { |
| 272 | /* Directory entry already exists, but we cannot fetch the |
| 273 | * fid it points to. */ |
| 274 | code = EIO; |
| 275 | } |
| 276 | /* make sure vrefCount bumped only if code == 0 */ |
| 277 | goto done; |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | /* if we create the file, we don't do any access checks, since |
| 282 | * that's how O_CREAT is supposed to work */ |
| 283 | if (adp->f.states & CForeign) { |
| 284 | origCBs = afs_allCBs; |
| 285 | origZaps = afs_allZaps; |
| 286 | } else { |
| 287 | origCBs = afs_evenCBs; /* if changes, we don't really have a callback */ |
| 288 | origZaps = afs_evenZaps; /* number of even numbered vnodes discarded */ |
| 289 | } |
| 290 | InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; |
| 291 | InStatus.ClientModTime = osi_Time(); |
| 292 | InStatus.Group = (afs_int32) afs_cr_gid(acred); |
| 293 | if (AFS_NFSXLATORREQ(acred)) { |
| 294 | /* |
| 295 | * XXX The following is mainly used to fix a bug in the HP-UX |
| 296 | * nfs client where they create files with mode of 0 without |
| 297 | * doing any setattr later on to fix it. * XXX |
| 298 | */ |
| 299 | #if defined(AFS_AIX_ENV) |
| 300 | if (attrs->va_mode != -1) { |
| 301 | #else |
| 302 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) |
| 303 | if (attrs->va_mask & AT_MODE) { |
| 304 | #else |
| 305 | if (attrs->va_mode != ((unsigned short)-1)) { |
| 306 | #endif |
| 307 | #endif |
| 308 | if (!attrs->va_mode) |
| 309 | attrs->va_mode = 0x1b6; /* XXX default mode: rw-rw-rw XXX */ |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | if (!AFS_IS_DISCONNECTED) { |
| 314 | /* If not disconnected, connect to the server.*/ |
| 315 | |
| 316 | InStatus.UnixModeBits = attrs->va_mode & 0xffff; /* only care about protection bits */ |
| 317 | do { |
| 318 | tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn); |
| 319 | if (tc) { |
| 320 | hostp = tc->parent->srvr->server; /* remember for callback processing */ |
| 321 | now = osi_Time(); |
| 322 | XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE); |
| 323 | RX_AFS_GUNLOCK(); |
| 324 | code = |
| 325 | RXAFS_CreateFile(rxconn, (struct AFSFid *)&adp->f.fid.Fid, |
| 326 | aname, &InStatus, (struct AFSFid *) |
| 327 | &newFid.Fid, OutFidStatus, OutDirStatus, |
| 328 | &CallBack, &tsync); |
| 329 | RX_AFS_GLOCK(); |
| 330 | XSTATS_END_TIME; |
| 331 | CallBack.ExpirationTime += now; |
| 332 | } else |
| 333 | code = -1; |
| 334 | } while (afs_Analyze |
| 335 | (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_CREATEFILE, |
| 336 | SHARED_LOCK, NULL)); |
| 337 | |
| 338 | if ((code == EEXIST || code == UAEEXIST) && |
| 339 | #ifdef AFS_SGI64_ENV |
| 340 | !(flags & VEXCL) |
| 341 | #else /* AFS_SGI64_ENV */ |
| 342 | aexcl == NONEXCL |
| 343 | #endif |
| 344 | ) { |
| 345 | /* if we get an EEXIST in nonexcl mode, just do a lookup */ |
| 346 | if (tdc) { |
| 347 | ReleaseSharedLock(&tdc->lock); |
| 348 | afs_PutDCache(tdc); |
| 349 | } |
| 350 | ReleaseWriteLock(&adp->lock); |
| 351 | |
| 352 | |
| 353 | #if defined(AFS_SGI64_ENV) |
| 354 | code = afs_lookup(VNODE_TO_FIRST_BHV((vnode_t *) adp), aname, avcp, |
| 355 | NULL, 0, NULL, acred); |
| 356 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) |
| 357 | code = afs_lookup(adp, aname, avcp, NULL, 0, NULL, acred); |
| 358 | #elif defined(UKERNEL) |
| 359 | code = afs_lookup(adp, aname, avcp, acred, 0); |
| 360 | #elif !defined(AFS_DARWIN_ENV) |
| 361 | code = afs_lookup(adp, aname, avcp, acred); |
| 362 | #endif |
| 363 | goto done; |
| 364 | } |
| 365 | |
| 366 | if (code) { |
| 367 | if (code < 0) { |
| 368 | afs_StaleVCache(adp); |
| 369 | } |
| 370 | ReleaseWriteLock(&adp->lock); |
| 371 | if (tdc) { |
| 372 | ReleaseSharedLock(&tdc->lock); |
| 373 | afs_PutDCache(tdc); |
| 374 | } |
| 375 | goto done; |
| 376 | } |
| 377 | |
| 378 | } else { |
| 379 | /* Generate a fake FID for disconnected mode. */ |
| 380 | newFid.Cell = adp->f.fid.Cell; |
| 381 | newFid.Fid.Volume = adp->f.fid.Fid.Volume; |
| 382 | afs_GenFakeFid(&newFid, VREG, 1); |
| 383 | } /* if (!AFS_IS_DISCON_RW) */ |
| 384 | |
| 385 | /* otherwise, we should see if we can make the change to the dir locally */ |
| 386 | if (tdc) |
| 387 | UpgradeSToWLock(&tdc->lock, 631); |
| 388 | if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) { |
| 389 | /* we can do it locally */ |
| 390 | ObtainWriteLock(&afs_xdcache, 291); |
| 391 | code = afs_dir_Create(tdc, aname, &newFid.Fid); |
| 392 | ReleaseWriteLock(&afs_xdcache); |
| 393 | if (code) { |
| 394 | ZapDCE(tdc); |
| 395 | DZap(tdc); |
| 396 | } |
| 397 | } |
| 398 | if (tdc) { |
| 399 | ReleaseWriteLock(&tdc->lock); |
| 400 | afs_PutDCache(tdc); |
| 401 | } |
| 402 | if (AFS_IS_DISCON_RW) |
| 403 | adp->f.m.LinkCount++; |
| 404 | |
| 405 | newFid.Cell = adp->f.fid.Cell; |
| 406 | newFid.Fid.Volume = adp->f.fid.Fid.Volume; |
| 407 | ReleaseWriteLock(&adp->lock); |
| 408 | volp = afs_FindVolume(&newFid, READ_LOCK); |
| 409 | |
| 410 | /* New tricky optimistic callback handling algorithm for file creation works |
| 411 | * as follows. We create the file essentially with no locks set at all. File |
| 412 | * server may thus handle operations from others cache managers as well as from |
| 413 | * this very own cache manager that reference the file in question before |
| 414 | * we managed to create the cache entry. However, if anyone else changes |
| 415 | * any of the status information for a file, we'll see afs_evenCBs increase |
| 416 | * (files always have even fids). If someone on this workstation manages |
| 417 | * to do something to the file, they'll end up having to create a cache |
| 418 | * entry for the new file. Either we'll find it once we've got the afs_xvcache |
| 419 | * lock set, or it was also *deleted* the vnode before we got there, in which case |
| 420 | * we will find evenZaps has changed, too. Thus, we only assume we have the right |
| 421 | * status information if no callbacks or vnode removals have occurred to even |
| 422 | * numbered files from the time the call started until the time that we got the xvcache |
| 423 | * lock set. Of course, this also assumes that any call that modifies a file first |
| 424 | * gets a write lock on the file's vnode, but if that weren't true, the whole cache manager |
| 425 | * would fail, since no call would be able to update the local vnode status after modifying |
| 426 | * a file on a file server. */ |
| 427 | ObtainWriteLock(&afs_xvcache, 138); |
| 428 | if (adp->f.states & CForeign) |
| 429 | finalZaps = afs_allZaps; /* do this before calling newvcache */ |
| 430 | else |
| 431 | finalZaps = afs_evenZaps; /* do this before calling newvcache */ |
| 432 | /* don't need to call RemoveVCB, since only path leaving a callback is the |
| 433 | * one where we pass through afs_NewVCache. Can't have queued a VCB unless |
| 434 | * we created and freed an entry between file creation time and here, and the |
| 435 | * freeing of the vnode will change evenZaps. Don't need to update the VLRU |
| 436 | * queue, since the find will only succeed in the event of a create race, and |
| 437 | * then the vcache will be at the front of the VLRU queue anyway... */ |
| 438 | if (!(tvc = afs_FindVCache(&newFid, 0, DO_STATS))) { |
| 439 | tvc = afs_NewVCache(&newFid, hostp); |
| 440 | if (tvc) { |
| 441 | int finalCBs; |
| 442 | ObtainWriteLock(&tvc->lock, 139); |
| 443 | |
| 444 | ObtainWriteLock(&afs_xcbhash, 489); |
| 445 | finalCBs = afs_evenCBs; |
| 446 | /* add the callback in */ |
| 447 | if (adp->f.states & CForeign) { |
| 448 | tvc->f.states |= CForeign; |
| 449 | finalCBs = afs_allCBs; |
| 450 | } |
| 451 | if (origCBs == finalCBs && origZaps == finalZaps) { |
| 452 | tvc->f.states |= CStatd; /* we've fake entire thing, so don't stat */ |
| 453 | tvc->f.states &= ~CBulkFetching; |
| 454 | if (!AFS_IS_DISCON_RW) { |
| 455 | tvc->cbExpires = CallBack.ExpirationTime; |
| 456 | afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp); |
| 457 | } |
| 458 | } else { |
| 459 | afs_StaleVCacheFlags(tvc, |
| 460 | AFS_STALEVC_CBLOCKED | AFS_STALEVC_CLEARCB, |
| 461 | CUnique); |
| 462 | } |
| 463 | ReleaseWriteLock(&afs_xcbhash); |
| 464 | if (AFS_IS_DISCON_RW) { |
| 465 | afs_DisconAddDirty(tvc, VDisconCreate, 0); |
| 466 | afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VREG); |
| 467 | } else { |
| 468 | afs_ProcessFS(tvc, OutFidStatus, treq); |
| 469 | } |
| 470 | |
| 471 | tvc->f.parent.vnode = adp->f.fid.Fid.Vnode; |
| 472 | tvc->f.parent.unique = adp->f.fid.Fid.Unique; |
| 473 | ReleaseWriteLock(&tvc->lock); |
| 474 | *avcp = tvc; |
| 475 | code = 0; |
| 476 | |
| 477 | } else { |
| 478 | /* Cannot create a new vcache. */ |
| 479 | code = EIO; |
| 480 | } |
| 481 | } else { |
| 482 | /* otherwise cache entry already exists, someone else must |
| 483 | * have created it. Comments used to say: "don't need write |
| 484 | * lock to *clear* these flags" but we should do it anyway. |
| 485 | * Code used to clear stat bit and callback, but I don't see |
| 486 | * the point -- we didn't have a create race, somebody else just |
| 487 | * snuck into NewVCache before we got here, probably a racing |
| 488 | * lookup. |
| 489 | */ |
| 490 | *avcp = tvc; |
| 491 | code = 0; |
| 492 | } |
| 493 | ReleaseWriteLock(&afs_xvcache); |
| 494 | |
| 495 | done: |
| 496 | AFS_DISCON_UNLOCK(); |
| 497 | |
| 498 | done3: |
| 499 | if (volp) |
| 500 | afs_PutVolume(volp, READ_LOCK); |
| 501 | |
| 502 | if (code == 0) { |
| 503 | if (afs_mariner) |
| 504 | afs_AddMarinerName(aname, *avcp); |
| 505 | /* return the new status in vattr */ |
| 506 | afs_CopyOutAttrs(*avcp, attrs); |
| 507 | if (afs_mariner) |
| 508 | afs_MarinerLog("store$Creating", *avcp); |
| 509 | } |
| 510 | |
| 511 | afs_PutFakeStat(&fakestate); |
| 512 | code = afs_CheckCode(code, treq, 20); |
| 513 | afs_DestroyReq(treq); |
| 514 | |
| 515 | done2: |
| 516 | osi_FreeSmallSpace(OutFidStatus); |
| 517 | osi_FreeSmallSpace(OutDirStatus); |
| 518 | return code; |
| 519 | } |
| 520 | |
| 521 | |
| 522 | /* |
| 523 | * Check to see if we can track the change locally: requires that |
| 524 | * we have sufficiently recent info in data cache. If so, we |
| 525 | * know the new DataVersion number, and place it correctly in both the |
| 526 | * data and stat cache entries. This routine returns 1 if we should |
| 527 | * do the operation locally, and 0 otherwise. |
| 528 | * |
| 529 | * This routine must be called with the stat cache entry write-locked, |
| 530 | * and dcache entry write-locked. |
| 531 | */ |
| 532 | int |
| 533 | afs_LocalHero(struct vcache *avc, struct dcache *adc, |
| 534 | AFSFetchStatus * astat, int aincr) |
| 535 | { |
| 536 | afs_int32 ok; |
| 537 | afs_hyper_t avers; |
| 538 | |
| 539 | AFS_STATCNT(afs_LocalHero); |
| 540 | hset64(avers, astat->dataVersionHigh, astat->DataVersion); |
| 541 | /* avers *is* the version number now, no matter what */ |
| 542 | |
| 543 | if (adc) { |
| 544 | /* does what's in the dcache *now* match what's in the vcache *now*, |
| 545 | * and do we have a valid callback? if not, our local copy is not "ok" */ |
| 546 | ok = (hsame(avc->f.m.DataVersion, adc->f.versionNo) && avc->callback |
| 547 | && (avc->f.states & CStatd) && avc->cbExpires >= osi_Time()); |
| 548 | } else { |
| 549 | ok = 0; |
| 550 | } |
| 551 | if (ok) { |
| 552 | /* check that the DV on the server is what we expect it to be */ |
| 553 | afs_hyper_t newDV; |
| 554 | hset(newDV, adc->f.versionNo); |
| 555 | hadd32(newDV, aincr); |
| 556 | if (!hsame(avers, newDV)) { |
| 557 | ok = 0; |
| 558 | } |
| 559 | } |
| 560 | #if defined(AFS_SGI_ENV) |
| 561 | osi_Assert(avc->v.v_type == VDIR); |
| 562 | #endif |
| 563 | /* The bulk status code used the length as a sequence number. */ |
| 564 | /* Don't update the vcache entry unless the stats are current. */ |
| 565 | if (avc->f.states & CStatd) { |
| 566 | afs_SetDataVersion(avc, &avers); |
| 567 | #ifdef AFS_64BIT_CLIENT |
| 568 | FillInt64(avc->f.m.Length, astat->Length_hi, astat->Length); |
| 569 | #else /* AFS_64BIT_CLIENT */ |
| 570 | avc->f.m.Length = astat->Length; |
| 571 | #endif /* AFS_64BIT_CLIENT */ |
| 572 | avc->f.m.Date = astat->ClientModTime; |
| 573 | } |
| 574 | if (ok) { |
| 575 | /* we've been tracking things correctly */ |
| 576 | adc->dflags |= DFEntryMod; |
| 577 | adc->f.versionNo = avers; |
| 578 | return 1; |
| 579 | } else { |
| 580 | if (adc) { |
| 581 | ZapDCE(adc); |
| 582 | DZap(adc); |
| 583 | } |
| 584 | if (avc->f.states & CStatd) { |
| 585 | osi_dnlc_purgedp(avc); |
| 586 | } |
| 587 | return 0; |
| 588 | } |
| 589 | } |