int afscp_CreateFile(const struct afscp_venusfid *dir, char *name, struct AFSStoreStatus *sst, struct afscp_venusfid **ret) { int code, i, j; struct AFSFid df = dir->fid; struct afscp_volume *vol; struct AFSFetchStatus dfst, fst; struct AFSVolSync vs; struct AFSCallBack cb; struct AFSFid ff; struct afscp_server *server; struct rx_connection *c; time_t now; if (dir == NULL || name == NULL || sst == NULL) { fprintf(stderr, "afscp_CreateFile called with NULL args, cannot continue\n"); return -1; } vol = afscp_VolumeById(dir->cell, dir->fid.Volume); if (vol == NULL) { afscp_errno = ENOENT; return -1; } code = ENOENT; for (i = 0; i < vol->nservers; i++) { server = afscp_ServerByIndex(vol->servers[i]); if (server && server->naddrs > 0) { for (j = 0; j < server->naddrs; j++) { c = afscp_ServerConnection(server, j); if (c == NULL) { break; } time(&now); code = RXAFS_CreateFile(c, &df, name, sst, &ff, &fst, &dfst, &cb, &vs); if (code >= 0) { break; } } } if (code >= 0) { break; } } if (code != 0) { _StatInvalidate(dir); afscp_errno = code; return -1; } _StatStuff(dir, &dfst); afscp_AddCallBack(server, &ff, &fst, &cb, now); if (ret != NULL) *ret = afscp_MakeFid(vol->cell, ff.Volume, ff.Vnode, ff.Unique); return 0; }
/* question: does afs_create need to set CDirty in the adp or the avc? * I think we can get away without it, but I'm not sure. Note that * afs_setattr is called in here for truncation. */ #ifdef AFS_SGI64_ENV int afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, int flags, int amode, struct vcache **avcp, afs_ucred_t *acred) #else /* AFS_SGI64_ENV */ int afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, enum vcexcl aexcl, int amode, struct vcache **avcp, afs_ucred_t *acred) #endif /* AFS_SGI64_ENV */ { afs_int32 origCBs, origZaps, finalZaps; struct vrequest *treq = NULL; afs_int32 code; struct afs_conn *tc; struct VenusFid newFid; struct AFSStoreStatus InStatus; struct AFSFetchStatus *OutFidStatus, *OutDirStatus; struct AFSVolSync tsync; struct AFSCallBack CallBack; afs_int32 now; struct dcache *tdc; afs_size_t offset, len; struct server *hostp = 0; struct vcache *tvc; struct volume *volp = 0; struct afs_fakestat_state fakestate; struct rx_connection *rxconn; XSTATS_DECLS; OSI_VC_CONVERT(adp); AFS_STATCNT(afs_create); OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus)); memset(&InStatus, 0, sizeof(InStatus)); if ((code = afs_CreateReq(&treq, acred))) goto done2; afs_Trace3(afs_iclSetp, CM_TRACE_CREATE, ICL_TYPE_POINTER, adp, ICL_TYPE_STRING, aname, ICL_TYPE_INT32, amode); afs_InitFakeStat(&fakestate); #ifdef AFS_SGI65_ENV /* If avcp is passed not null, it's the old reference to this file. * We can use this to avoid create races. For now, just decrement * the reference count on it. */ if (*avcp) { AFS_RELE(AFSTOV(*avcp)); *avcp = NULL; } #endif if (strlen(aname) > AFSNAMEMAX) { code = ENAMETOOLONG; goto done3; } if (!afs_ENameOK(aname)) { code = EINVAL; goto done3; } switch (attrs->va_type) { case VBLK: case VCHR: #if !defined(AFS_SUN5_ENV) case VSOCK: #endif case VFIFO: /* We don't support special devices or FIFOs */ code = EINVAL; goto done3; default: ; } AFS_DISCON_LOCK(); code = afs_EvalFakeStat(&adp, &fakestate, treq); if (code) goto done; tagain: code = afs_VerifyVCache(adp, treq); if (code) goto done; /** If the volume is read-only, return error without making an RPC to the * fileserver */ if (adp->f.states & CRO) { code = EROFS; goto done; } if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { code = ENETDOWN; goto done; } tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1); ObtainWriteLock(&adp->lock, 135); if (tdc) ObtainSharedLock(&tdc->lock, 630); /* * Make sure that the data in the cache is current. We may have * received a callback while we were waiting for the write lock. */ if (!(adp->f.states & CStatd) || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) { ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto tagain; } if (tdc) { /* see if file already exists. If it does, we only set * the size attributes (to handle O_TRUNC) */ code = afs_dir_Lookup(tdc, aname, &newFid.Fid); /* use dnlc first xxx */ if (code == 0) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); ReleaseWriteLock(&adp->lock); #ifdef AFS_SGI64_ENV if (flags & VEXCL) { #else if (aexcl != NONEXCL) { #endif code = EEXIST; /* file exists in excl mode open */ goto done; } /* found the file, so use it */ newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; tvc = NULL; if (newFid.Fid.Unique == 0) { tvc = afs_LookupVCache(&newFid, treq, NULL, adp, aname); } if (!tvc) /* lookup failed or wasn't called */ tvc = afs_GetVCache(&newFid, treq, NULL, NULL); if (tvc) { /* if the thing exists, we need the right access to open it. * we must check that here, since no other checks are * made by the open system call */ len = attrs->va_size; /* only do the truncate */ /* * We used to check always for READ access before; the * problem is that we will fail if the existing file * has mode -w-w-w, which is wrong. */ if ((amode & VREAD) && !afs_AccessOK(tvc, PRSFS_READ, treq, CHECK_MODE_BITS)) { afs_PutVCache(tvc); code = EACCES; goto done; } #if defined(AFS_DARWIN80_ENV) if ((amode & VWRITE) || VATTR_IS_ACTIVE(attrs, va_data_size)) #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if ((amode & VWRITE) || (attrs->va_mask & AT_SIZE)) #else if ((amode & VWRITE) || len != 0xffffffff) #endif { /* needed for write access check */ tvc->f.parent.vnode = adp->f.fid.Fid.Vnode; tvc->f.parent.unique = adp->f.fid.Fid.Unique; /* need write mode for these guys */ if (!afs_AccessOK (tvc, PRSFS_WRITE, treq, CHECK_MODE_BITS)) { afs_PutVCache(tvc); code = EACCES; goto done; } } #if defined(AFS_DARWIN80_ENV) if (VATTR_IS_ACTIVE(attrs, va_data_size)) #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if (attrs->va_mask & AT_SIZE) #else if (len != 0xffffffff) #endif { if (vType(tvc) != VREG) { afs_PutVCache(tvc); code = EISDIR; goto done; } /* do a truncate */ #if defined(AFS_DARWIN80_ENV) VATTR_INIT(attrs); VATTR_SET_SUPPORTED(attrs, va_data_size); VATTR_SET_ACTIVE(attrs, va_data_size); #elif defined(UKERNEL) attrs->va_mask = ATTR_SIZE; #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) attrs->va_mask = AT_SIZE; #else VATTR_NULL(attrs); #endif attrs->va_size = len; ObtainWriteLock(&tvc->lock, 136); tvc->f.states |= CCreating; ReleaseWriteLock(&tvc->lock); #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) #if defined(AFS_SGI64_ENV) code = afs_setattr(VNODE_TO_FIRST_BHV((vnode_t *) tvc), attrs, 0, acred); #else code = afs_setattr(tvc, attrs, 0, acred); #endif /* AFS_SGI64_ENV */ #else /* SUN5 || SGI */ code = afs_setattr(tvc, attrs, acred); #endif /* SUN5 || SGI */ ObtainWriteLock(&tvc->lock, 137); tvc->f.states &= ~CCreating; ReleaseWriteLock(&tvc->lock); if (code) { afs_PutVCache(tvc); goto done; } } *avcp = tvc; } else code = ENOENT; /* shouldn't get here */ /* make sure vrefCount bumped only if code == 0 */ goto done; } } /* if we create the file, we don't do any access checks, since * that's how O_CREAT is supposed to work */ if (adp->f.states & CForeign) { origCBs = afs_allCBs; origZaps = afs_allZaps; } else { origCBs = afs_evenCBs; /* if changes, we don't really have a callback */ origZaps = afs_evenZaps; /* number of even numbered vnodes discarded */ } InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; InStatus.ClientModTime = osi_Time(); InStatus.Group = (afs_int32) afs_cr_gid(acred); if (AFS_NFSXLATORREQ(acred)) { /* * XXX The following is mainly used to fix a bug in the HP-UX * nfs client where they create files with mode of 0 without * doing any setattr later on to fix it. * XXX */ #if defined(AFS_AIX_ENV) if (attrs->va_mode != -1) { #else #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) if (attrs->va_mask & AT_MODE) { #else if (attrs->va_mode != ((unsigned short)-1)) { #endif #endif if (!attrs->va_mode) attrs->va_mode = 0x1b6; /* XXX default mode: rw-rw-rw XXX */ } } if (!AFS_IS_DISCONNECTED) { /* If not disconnected, connect to the server.*/ InStatus.UnixModeBits = attrs->va_mode & 0xffff; /* only care about protection bits */ do { tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn); if (tc) { hostp = tc->srvr->server; /* remember for callback processing */ now = osi_Time(); XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE); RX_AFS_GUNLOCK(); code = RXAFS_CreateFile(rxconn, (struct AFSFid *)&adp->f.fid.Fid, aname, &InStatus, (struct AFSFid *) &newFid.Fid, OutFidStatus, OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; CallBack.ExpirationTime += now; } else code = -1; } while (afs_Analyze (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_CREATEFILE, SHARED_LOCK, NULL)); if ((code == EEXIST || code == UAEEXIST) && #ifdef AFS_SGI64_ENV !(flags & VEXCL) #else /* AFS_SGI64_ENV */ aexcl == NONEXCL #endif ) { /* if we get an EEXIST in nonexcl mode, just do a lookup */ if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } ReleaseWriteLock(&adp->lock); #if defined(AFS_SGI64_ENV) code = afs_lookup(VNODE_TO_FIRST_BHV((vnode_t *) adp), aname, avcp, NULL, 0, NULL, acred); #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) code = afs_lookup(adp, aname, avcp, NULL, 0, NULL, acred); #elif defined(UKERNEL) code = afs_lookup(adp, aname, avcp, acred, 0); #elif !defined(AFS_DARWIN_ENV) code = afs_lookup(adp, aname, avcp, acred); #endif goto done; } if (code) { if (code < 0) { ObtainWriteLock(&afs_xcbhash, 488); afs_DequeueCallback(adp); adp->f.states &= ~CStatd; ReleaseWriteLock(&afs_xcbhash); osi_dnlc_purgedp(adp); } ReleaseWriteLock(&adp->lock); if (tdc) { ReleaseSharedLock(&tdc->lock); afs_PutDCache(tdc); } goto done; } } else { /* Generate a fake FID for disconnected mode. */ newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; afs_GenFakeFid(&newFid, VREG, 1); } /* if (!AFS_IS_DISCON_RW) */ /* otherwise, we should see if we can make the change to the dir locally */ if (tdc) UpgradeSToWLock(&tdc->lock, 631); if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) { /* we can do it locally */ ObtainWriteLock(&afs_xdcache, 291); code = afs_dir_Create(tdc, aname, &newFid.Fid); ReleaseWriteLock(&afs_xdcache); if (code) { ZapDCE(tdc); DZap(tdc); } } if (tdc) { ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } if (AFS_IS_DISCON_RW) adp->f.m.LinkCount++; newFid.Cell = adp->f.fid.Cell; newFid.Fid.Volume = adp->f.fid.Fid.Volume; ReleaseWriteLock(&adp->lock); volp = afs_FindVolume(&newFid, READ_LOCK); /* New tricky optimistic callback handling algorithm for file creation works * as follows. We create the file essentially with no locks set at all. File * server may thus handle operations from others cache managers as well as from * this very own cache manager that reference the file in question before * we managed to create the cache entry. However, if anyone else changes * any of the status information for a file, we'll see afs_evenCBs increase * (files always have even fids). If someone on this workstation manages * to do something to the file, they'll end up having to create a cache * entry for the new file. Either we'll find it once we've got the afs_xvcache * lock set, or it was also *deleted* the vnode before we got there, in which case * we will find evenZaps has changed, too. Thus, we only assume we have the right * status information if no callbacks or vnode removals have occurred to even * numbered files from the time the call started until the time that we got the xvcache * lock set. Of course, this also assumes that any call that modifies a file first * gets a write lock on the file's vnode, but if that weren't true, the whole cache manager * would fail, since no call would be able to update the local vnode status after modifying * a file on a file server. */ ObtainWriteLock(&afs_xvcache, 138); if (adp->f.states & CForeign) finalZaps = afs_allZaps; /* do this before calling newvcache */ else finalZaps = afs_evenZaps; /* do this before calling newvcache */ /* don't need to call RemoveVCB, since only path leaving a callback is the * one where we pass through afs_NewVCache. Can't have queued a VCB unless * we created and freed an entry between file creation time and here, and the * freeing of the vnode will change evenZaps. Don't need to update the VLRU * queue, since the find will only succeed in the event of a create race, and * then the vcache will be at the front of the VLRU queue anyway... */ if (!(tvc = afs_FindVCache(&newFid, 0, DO_STATS))) { tvc = afs_NewVCache(&newFid, hostp); if (tvc) { int finalCBs; ObtainWriteLock(&tvc->lock, 139); ObtainWriteLock(&afs_xcbhash, 489); finalCBs = afs_evenCBs; /* add the callback in */ if (adp->f.states & CForeign) { tvc->f.states |= CForeign; finalCBs = afs_allCBs; } if (origCBs == finalCBs && origZaps == finalZaps) { tvc->f.states |= CStatd; /* we've fake entire thing, so don't stat */ tvc->f.states &= ~CBulkFetching; if (!AFS_IS_DISCON_RW) { tvc->cbExpires = CallBack.ExpirationTime; afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp); } } else { afs_DequeueCallback(tvc); tvc->f.states &= ~(CStatd | CUnique); tvc->callback = 0; if (tvc->f.fid.Fid.Vnode & 1 || (vType(tvc) == VDIR)) osi_dnlc_purgedp(tvc); } ReleaseWriteLock(&afs_xcbhash); if (AFS_IS_DISCON_RW) { afs_DisconAddDirty(tvc, VDisconCreate, 0); afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VREG); } else { afs_ProcessFS(tvc, OutFidStatus, treq); } tvc->f.parent.vnode = adp->f.fid.Fid.Vnode; tvc->f.parent.unique = adp->f.fid.Fid.Unique; #if !defined(UKERNEL) if (volp && (volp->states & VPartVisible)) tvc->f.states |= CPartVisible; #endif ReleaseWriteLock(&tvc->lock); *avcp = tvc; code = 0; } else code = ENOENT; } else { /* otherwise cache entry already exists, someone else must * have created it. Comments used to say: "don't need write * lock to *clear* these flags" but we should do it anyway. * Code used to clear stat bit and callback, but I don't see * the point -- we didn't have a create race, somebody else just * snuck into NewVCache before we got here, probably a racing * lookup. */ *avcp = tvc; code = 0; } ReleaseWriteLock(&afs_xvcache); done: AFS_DISCON_UNLOCK(); done3: if (volp) afs_PutVolume(volp, READ_LOCK); if (code == 0) { if (afs_mariner) afs_AddMarinerName(aname, *avcp); /* return the new status in vattr */ afs_CopyOutAttrs(*avcp, attrs); if (afs_mariner) afs_MarinerLog("store$Creating", *avcp); } afs_PutFakeStat(&fakestate); code = afs_CheckCode(code, treq, 20); afs_DestroyReq(treq); done2: osi_FreeSmallSpace(OutFidStatus); osi_FreeSmallSpace(OutDirStatus); return code; } /* * Check to see if we can track the change locally: requires that * we have sufficiently recent info in data cache. If so, we * know the new DataVersion number, and place it correctly in both the * data and stat cache entries. This routine returns 1 if we should * do the operation locally, and 0 otherwise. * * This routine must be called with the stat cache entry write-locked, * and dcache entry write-locked. */ int afs_LocalHero(struct vcache *avc, struct dcache *adc, AFSFetchStatus * astat, int aincr) { afs_int32 ok; afs_hyper_t avers; AFS_STATCNT(afs_LocalHero); hset64(avers, astat->dataVersionHigh, astat->DataVersion); /* avers *is* the version number now, no matter what */ if (adc) { /* does what's in the dcache *now* match what's in the vcache *now*, * and do we have a valid callback? if not, our local copy is not "ok" */ ok = (hsame(avc->f.m.DataVersion, adc->f.versionNo) && avc->callback && (avc->f.states & CStatd) && avc->cbExpires >= osi_Time()); } else { ok = 0; } if (ok) { /* check that the DV on the server is what we expect it to be */ afs_hyper_t newDV; hset(newDV, adc->f.versionNo); hadd32(newDV, aincr); if (!hsame(avers, newDV)) { ok = 0; } } #if defined(AFS_SGI_ENV) osi_Assert(avc->v.v_type == VDIR); #endif /* The bulk status code used the length as a sequence number. */ /* Don't update the vcache entry unless the stats are current. */ if (avc->f.states & CStatd) { hset(avc->f.m.DataVersion, avers); #ifdef AFS_64BIT_CLIENT FillInt64(avc->f.m.Length, astat->Length_hi, astat->Length); #else /* AFS_64BIT_CLIENT */ avc->f.m.Length = astat->Length; #endif /* AFS_64BIT_CLIENT */ avc->f.m.Date = astat->ClientModTime; } if (ok) { /* we've been tracking things correctly */ adc->dflags |= DFEntryMod; adc->f.versionNo = avers; return 1; } else { if (adc) { ZapDCE(adc); DZap(adc); } if (avc->f.states & CStatd) { osi_dnlc_purgedp(avc); } return 0; } }
int main(int argc, char **argv) { char scell[MAXCELLCHARS], dcell[MAXCELLCHARS]; afs_uint32 ssrv, dsrv; char *databuffer, *srcf = NULL, *destd = NULL, *destf = NULL, *destpath = NULL; struct stat statbuf; struct AFSStoreStatus sst; struct AFSFetchStatus fst, dfst; struct AFSVolSync vs; struct AFSCallBack scb, dcb; struct AFSFid sf, dd, df; int filesz = 0; int ch, blksize, bytesremaining, bytes; struct timeval start, finish; struct timezone tz; struct rx_securityClass *ssc = 0, *dsc = 0; int sscindex, dscindex; struct rx_connection *sconn = NULL, *dconn = NULL; struct rx_call *scall = NULL, *dcall = NULL; int code = 0, fetchcode, storecode, printcallerrs = 0; int slcl = 0, dlcl = 0, unlock = 0; int sfd = 0, dfd = 0, unauth = 0; struct AFSCBFids theFids; struct AFSCBs theCBs; blksize = 8 * 1024; while ((ch = getopt(argc, argv, "iouUb:")) != -1) { switch (ch) { case 'b': blksize = atoi(optarg); break; case 'i': slcl = 1; break; case 'o': dlcl = 1; break; case 'u': unauth = 1; break; case 'U': unlock = 1; break; default: printf("Unknown option '%c'\n", ch); exit(1); } } if (argc - optind + unlock < 2) { fprintf(stderr, "Usage: afscp [-i|-o]] [-b xfersz] [-u] [-U] source [dest]\n"); fprintf(stderr, " -b Set block size\n"); fprintf(stderr, " -i Source is local (copy into AFS)\n"); fprintf(stderr, " -o Dest is local (copy out of AFS)\n"); fprintf(stderr, " -u Run unauthenticated\n"); fprintf(stderr, " -U Send an unlock request for source. (dest path not required)\n"); fprintf(stderr, "source and dest can be paths or specified as:\n"); fprintf(stderr, " @afs:cellname:servername:volume:vnode:uniq\n"); exit(1); } srcf = argv[optind++]; if (!unlock) { destpath = argv[optind++]; destd = strdup(destpath); if (!destd) { perror("strdup"); exit(1); } if ((destf = strrchr(destd, '/'))) { *destf++ = 0; } else { destf = destd; destd = "."; } } else if (slcl) { fprintf(stderr, "-i and -U cannot be used together\n"); } if (!slcl && statfile(srcf, scell, &ssrv, &sf)) { fprintf(stderr, "Cannot get attributes of %s\n", srcf); exit(1); } if (!unlock && !dlcl && statfile(destd, dcell, &dsrv, &dd)) { fprintf(stderr, "Cannot get attributes of %s\n", destd); exit(1); } if ((databuffer = malloc(blksize)) == NULL) { perror("malloc"); exit(1); } if (do_rx_Init()) exit(1); if (start_cb_server()) { printf("Cannot start callback service\n"); goto Fail_rx; } if (!slcl) { sscindex = scindex_RXKAD; if (unauth || (ssc = get_sc(scell)) == NULL) { ssc = rxnull_NewClientSecurityObject(); sscindex = scindex_NULL; /*printf("Cannot get authentication for cell %s; running unauthenticated\n", scell); */ } sscindex = scindex_NULL; if ((sconn = rx_NewConnection(ssrv, htons(AFSCONF_FILEPORT), 1, ssc, sscindex)) == NULL) { struct in_addr s; s.s_addr = ssrv; printf("Cannot initialize rx connection to source server (%s)\n", inet_ntoa(s)); goto Fail_sc; } } if (!dlcl && !unlock) { if (!slcl && ssrv == dsrv) { dconn = sconn; dsc = NULL; } else { if (slcl || strcmp(scell, dcell)) { dscindex = scindex_RXKAD; if (unauth || (dsc = get_sc(dcell)) == NULL) { dsc = rxnull_NewClientSecurityObject(); dscindex = scindex_NULL; /*printf("Cannot get authentication for cell %s; running unauthenticated\n", dcell); */ } dscindex = scindex_NULL; } else { dsc = ssc; dscindex = sscindex; } if ((dconn = rx_NewConnection(dsrv, htons(AFSCONF_FILEPORT), 1, dsc, dscindex)) == NULL) { struct in_addr s; s.s_addr = dsrv; printf ("Cannot initialize rx connection to dest server (%s)\n", inet_ntoa(s)); goto Fail_sconn; } } } memset(&sst, 0, sizeof(struct AFSStoreStatus)); if (dlcl && !unlock) { dfd = open(destpath, O_RDWR | O_CREAT | O_EXCL, 0666); if (dfd < 0 && errno == EEXIST) { printf("%s already exists, overwriting\n", destpath); dfd = open(destpath, O_RDWR | O_TRUNC, 0666); if (dfd < 0) { fprintf(stderr, "Cannot open %s (%s)\n", destpath, afs_error_message(errno)); goto Fail_dconn; } } else if (dfd < 0) { fprintf(stderr, "Cannot open %s (%s)\n", destpath, afs_error_message(errno)); goto Fail_dconn; } } else if (!unlock) { if ((code = RXAFS_CreateFile(dconn, &dd, destf, &sst, &df, &fst, &dfst, &dcb, &vs))) { if (code == EEXIST) { printf("%s already exits, overwriting\n", destpath); if (statfile(destpath, dcell, &dsrv, &df)) fprintf(stderr, "Cannot get attributes of %s\n", destpath); else code = 0; } else { printf("Cannot create %s (%s)\n", destpath, afs_error_message(code)); if (code) goto Fail_dconn; } } } if (slcl) { sfd = open(srcf, O_RDONLY, 0); if (sfd < 0) { fprintf(stderr, "Cannot open %s (%s)\n", srcf, afs_error_message(errno)); goto Fail_dconn; } if (fstat(sfd, &statbuf) < 0) { fprintf(stderr, "Cannot stat %s (%s)\n", srcf, afs_error_message(errno)); close(sfd); goto Fail_dconn; } } else { if ((code = RXAFS_FetchStatus(sconn, &sf, &fst, &scb, &vs))) { printf("Cannot fetchstatus of %d.%d (%s)\n", sf.Volume, sf.Vnode, afs_error_message(code)); goto Fail_dconn; } } if (slcl) { filesz = statbuf.st_size; } else { filesz = fst.Length; } printcallerrs = 0; fetchcode = 0; storecode = 0; if (!slcl && !unlock) scall = rx_NewCall(sconn); if (!dlcl && !unlock) dcall = rx_NewCall(dconn); gettimeofday(&start, &tz); if (unlock) { if (fst.lockCount) { printf("Sending 1 unlock for %s (%d locks)\n", srcf, fst.lockCount); if ((code = RXAFS_ReleaseLock(sconn, &sf, &vs))) { printf("Unable to unlock %s (%s)\n", srcf, afs_error_message(code)); } } else { printf("No locks for %s\n", srcf); } fetchcode = code; goto Finish; } if (!slcl) { if ((code = StartRXAFS_FetchData(scall, &sf, 0, filesz))) { printf("Unable to fetch data from %s (%s)\n", srcf, afs_error_message(code)); goto Fail_call; } } if (!dlcl) { if (slcl) { sst.Mask = AFS_SETMODTIME | AFS_SETMODE; sst.ClientModTime = statbuf.st_mtime; sst.UnixModeBits = statbuf.st_mode & ~(S_IFMT | S_ISUID | S_ISGID); } else { sst.Mask = AFS_SETMODTIME | AFS_SETMODE; sst.ClientModTime = fst.ClientModTime; sst.UnixModeBits = fst.UnixModeBits & ~(S_IFMT | S_ISUID | S_ISGID); } if ((code = StartRXAFS_StoreData(dcall, &df, &sst, 0, filesz, filesz))) { printf("Unable to store data to %s (%s)\n", destpath, afs_error_message(code)); goto Fail_call; } } if (slcl) { bytesremaining = statbuf.st_size; } else { rx_Read(scall, (char *)&bytesremaining, sizeof(afs_int32)); bytesremaining = ntohl(bytesremaining); } while (bytesremaining > 0) { /*printf("%d bytes remaining\n",bytesremaining); */ if (slcl) { if ((bytes = read(sfd, databuffer, min(blksize, bytesremaining))) <= 0) { fetchcode = errno; break; } } else { if ((bytes = rx_Read(scall, databuffer, min(blksize, bytesremaining))) <= 0) break; } if (dlcl) { if (write(dfd, databuffer, bytes) != bytes) { storecode = errno; break; } } else { if (rx_Write(dcall, databuffer, bytes) != bytes) break; } bytesremaining -= bytes; /*printf("%d bytes copied\n",bytes); */ } if (bytesremaining > 0) { printf("Some network error occured while copying data\n"); goto Fail_call; } if (!slcl) fetchcode = EndRXAFS_FetchData(scall, &fst, &scb, &vs); if (!dlcl) storecode = EndRXAFS_StoreData(dcall, &fst, &vs); printcallerrs = 1; Fail_call: if (slcl) { if (close(sfd) && !fetchcode) fetchcode = errno; } else { fetchcode = rx_EndCall(scall, fetchcode); } if (fetchcode && printcallerrs) printf("Error returned from fetch: %s\n", afs_error_message(fetchcode)); if (dlcl) { if (close(dfd) && !storecode) storecode = errno; } else if (!unlock) { storecode = rx_EndCall(dcall, storecode); } if (storecode && printcallerrs) printf("Error returned from store: %s\n", afs_error_message(storecode)); Finish: gettimeofday(&finish, &tz); if (!slcl) { theFids.AFSCBFids_len = 1; theFids.AFSCBFids_val = &sf; theCBs.AFSCBs_len = 1; theCBs.AFSCBs_val = &scb; scb.CallBackType = CB_DROPPED; if ((code = RXAFS_GiveUpCallBacks(sconn, &theFids, &theCBs))) printf("Could not give up source callback: %s\n", afs_error_message(code)); } if (!dlcl) { theFids.AFSCBFids_len = 1; theFids.AFSCBFids_val = &df; theCBs.AFSCBs_len = 1; theCBs.AFSCBs_val = &dcb; dcb.CallBackType = CB_DROPPED; if ((code = RXAFS_GiveUpCallBacks(dconn, &theFids, &theCBs))) printf("Could not give up target callback: %s\n", afs_error_message(code)); } if (code == 0) code = storecode; if (code == 0) code = fetchcode; Fail_dconn: if (!dlcl && !unlock && (slcl || dconn != sconn)) rx_DestroyConnection(dconn); Fail_sconn: if (!slcl) rx_DestroyConnection(sconn); Fail_sc: if (dsc && dsc != ssc) RXS_Close(dsc); if (ssc) RXS_Close(ssc); Fail_rx: rx_Finalize(); free(databuffer); if (printcallerrs && !unlock) { double rate, size, time; if (finish.tv_sec == start.tv_sec) { printf("Copied %d bytes in %d microseconds\n", filesz, (int)(finish.tv_usec - start.tv_usec)); } else { printf("Copied %d bytes in %d seconds\n", filesz, (int)(finish.tv_sec - start.tv_sec)); } size = filesz / 1024.0; time = finish.tv_sec - start.tv_sec + (finish.tv_usec - start.tv_usec) / 1e+06; rate = size / time; printf("Transfer rate %g Kbytes/sec\n", rate); } exit(code != 0); }
/*! * Handles all the reconnection details: * - Get all the details about the vnode: name, fid, and parent dir fid. * - Send data to server. * - Handle errors. * - Reorder vhash and dcaches in their hashes, using the newly acquired fid. */ int afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq, afs_ucred_t *acred) { char *tname = NULL, *ttargetName = NULL; struct AFSStoreStatus InStatus; struct AFSFetchStatus OutFidStatus, OutDirStatus; struct VenusFid pdir_fid, newFid; struct AFSCallBack CallBack; struct AFSVolSync tsync; struct vcache *tdp = NULL, *tvc = NULL; struct dcache *tdc = NULL; struct afs_conn *tc; struct rx_connection *rxconn; afs_int32 hash, new_hash, index; afs_size_t tlen; int code, op = 0; XSTATS_DECLS; tname = afs_osi_Alloc(AFSNAMEMAX); if (!tname) return ENOMEM; code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp); if (code) goto end; /* This data may also be in linkData, but then we have to deal with * the joy of terminating NULLs and . and file modes. So just get * it from the dcache where it won't have been fiddled with. */ if (vType(avc) == VLNK) { afs_size_t offset; struct dcache *tdc; struct osi_file *tfile; tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0); if (!tdc) { code = ENOENT; goto end; } if (tlen > 1024) { afs_PutDCache(tdc); code = EFAULT; goto end; } tlen++; /* space for NULL */ ttargetName = afs_osi_Alloc(tlen); if (!ttargetName) { afs_PutDCache(tdc); return ENOMEM; } ObtainReadLock(&tdc->lock); tfile = afs_CFileOpen(&tdc->f.inode); code = afs_CFileRead(tfile, 0, ttargetName, tlen); ttargetName[tlen-1] = '\0'; afs_CFileClose(tfile); ReleaseReadLock(&tdc->lock); afs_PutDCache(tdc); } /* Set status. */ InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP; InStatus.ClientModTime = avc->f.m.Date; InStatus.Owner = avc->f.m.Owner; InStatus.Group = (afs_int32) afs_cr_gid(acred); /* Only care about protection bits. */ InStatus.UnixModeBits = avc->f.m.Mode & 0xffff; do { tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK, &rxconn); if (tc) { switch (vType(avc)) { case VREG: /* Make file on server. */ op = AFS_STATS_FS_RPCIDX_CREATEFILE; XSTATS_START_TIME(op); RX_AFS_GUNLOCK(); code = RXAFS_CreateFile(tc->id, (struct AFSFid *)&tdp->f.fid.Fid, tname, &InStatus, (struct AFSFid *) &newFid.Fid, &OutFidStatus, &OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; break; case VDIR: /* Make dir on server. */ op = AFS_STATS_FS_RPCIDX_MAKEDIR; XSTATS_START_TIME(op); RX_AFS_GUNLOCK(); code = RXAFS_MakeDir(rxconn, (struct AFSFid *) &tdp->f.fid.Fid, tname, &InStatus, (struct AFSFid *) &newFid.Fid, &OutFidStatus, &OutDirStatus, &CallBack, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; break; case VLNK: /* Make symlink on server. */ op = AFS_STATS_FS_RPCIDX_SYMLINK; XSTATS_START_TIME(op); RX_AFS_GUNLOCK(); code = RXAFS_Symlink(rxconn, (struct AFSFid *) &tdp->f.fid.Fid, tname, ttargetName, &InStatus, (struct AFSFid *) &newFid.Fid, &OutFidStatus, &OutDirStatus, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; break; default: op = AFS_STATS_FS_RPCIDX_CREATEFILE; code = 1; break; } } else code = -1; } while (afs_Analyze(tc, rxconn, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL)); /* TODO: Handle errors. */ if (code) { /* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */ goto end; } /* The rpc doesn't set the cell number. */ newFid.Cell = avc->f.fid.Cell; /* * Change the fid in the dir entry. */ /* Seek the dir's dcache. */ tdc = afs_FindDCacheByFid(&tdp->f.fid); if (tdc) { /* And now change the fid in the parent dir entry. */ afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode); afs_PutDCache(tdc); } if (vType(avc) == VDIR) { /* Change fid in the dir for the "." entry. ".." has alredy been * handled by afs_FixChildrenFids when processing the parent dir. */ tdc = afs_FindDCacheByFid(&avc->f.fid); if (tdc) { afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode); if (avc->f.m.LinkCount >= 2) /* For non empty dirs, fix children's parentVnode and * parentUnique reference. */ afs_FixChildrenFids(&avc->f.fid, &newFid); afs_PutDCache(tdc); } } /* Recompute hash chain positions for vnode and dcaches. * Then change to the new FID. */ /* The vcache goes first. */ ObtainWriteLock(&afs_xvcache, 735); /* Old fid hash. */ hash = VCHash(&avc->f.fid); /* New fid hash. */ new_hash = VCHash(&newFid); /* Remove hash from old position. */ /* XXX: not checking array element contents. It shouldn't be empty. * If it oopses, then something else might be wrong. */ if (afs_vhashT[hash] == avc) { /* First in hash chain (might be the only one). */ afs_vhashT[hash] = avc->hnext; } else { /* More elements in hash chain. */ for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) { if (tvc->hnext == avc) { tvc->hnext = avc->hnext; break; } } } /* if (!afs_vhashT[i]->hnext) */ QRemove(&avc->vhashq); /* Insert hash in new position. */ avc->hnext = afs_vhashT[new_hash]; afs_vhashT[new_hash] = avc; QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq); ReleaseWriteLock(&afs_xvcache); /* Do the same thing for all dcaches. */ hash = DVHash(&avc->f.fid); ObtainWriteLock(&afs_xdcache, 743); for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) { hash = afs_dvnextTbl[index]; tdc = afs_GetValidDSlot(index); ReleaseReadLock(&tdc->tlock); if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) { if (!FidCmp(&tdc->f.fid, &avc->f.fid)) { /* Safer but slower. */ afs_HashOutDCache(tdc, 0); /* Put dcache in new positions in the dchash and dvhash. */ new_hash = DCHash(&newFid, tdc->f.chunk); afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash]; afs_dchashTbl[new_hash] = tdc->index; new_hash = DVHash(&newFid); afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash]; afs_dvhashTbl[new_hash] = tdc->index; afs_indexUnique[tdc->index] = newFid.Fid.Unique; memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid)); } /* if fid match */ } /* if uniquifier match */ if (tdc) afs_PutDCache(tdc); } /* for all dcaches in this hash bucket */ ReleaseWriteLock(&afs_xdcache); /* Now we can set the new fid. */ memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid)); end: if (tdp) afs_PutVCache(tdp); afs_osi_Free(tname, AFSNAMEMAX); if (ttargetName) afs_osi_Free(ttargetName, tlen); return code; }