/* for dfblk only */ static Memblk* getmelted(uint isdir, uint type, daddrt *addrp, int *chg) { Memblk *b, *nb; *chg = 0; if(*addrp == 0){ b = dballoc(type); *addrp = b->addr; *chg = 1; return b; } b = dbget(type, *addrp); nb = nil; if(!b->frozen) return b; if(catcherror()){ mbput(b); mbput(nb); error(nil); } nb = dbdup(b); assert(type == b->type); if(isdir && type == DBdata) dupdentries(nb->d.data, Dblkdatasz/Daddrsz); USED(&nb); /* for error() */ *addrp = nb->addr; *chg = 1; dbput(b, b->type, b->addr); noerror(); mbput(b); return nb; }
/* * Drop one disk reference for f and reclaim its storage if it's gone. * The given memory reference is not released. * For directories, all files contained have their disk references adjusted, * and they are also reclaimed if no further references exist. * * NB: Time ago, directories were not in compact form (they had holes * due to removals) and this had a bug while reclaiming that could lead * to double frees of disk blocks. * The bug was fixed, but since then, directories have changed again to * have holes. If the a double free happens again, this is the place where * to look, besides dbdup and dfchdentry. */ int dfput(Memblk *f) { int i; Memblk *b; long tot; isfile(f); dKprint("dfput %H\n", f); /* * Remove children if it's the last disk ref before we drop data blocks. * No new disk refs may be added, so there's no race here. */ tot = 0; if(dbgetref(f->addr) == 1 && (f->d.mode&DMDIR) != 0){ rwlock(f, Wr); if(catcherror()){ rwunlock(f, Wr); error(nil); } for(i = 0; i < f->d.ndents; i++){ b = dfchild(f, i); if(!catcherror()){ tot += dfput(b); noerror(); } mbput(b); } noerror(); rwunlock(f, Wr); } if(dbput(f, f->type, f->addr) == 0) tot++; return tot; }
/* * Caller walked down p, and now requires the nth element to be * melted, and wlocked for writing. (nth count starts at 1); * * Return the path with the version of f that we must use, * locked for writing and melted. * References kept in the path are traded for the ones returned. */ Path* dfmelt(Path **pp, int nth) { int i; Memblk *f, **fp, *nf; Path *p; ownpath(pp); p = *pp; assert(nth >= 1 && p->nf >= nth && p->nf >= 2); assert(p->f[0] == fs->root); fp = &p->f[nth-1]; /* * 1. Optimistic: Try to get a loaded melted version for f. */ followmelted(fp, Wr); f = *fp; if(!f->frozen) return p; ainc(&fs->nmelts); rwunlock(f, Wr); /* * 2. Realistic: * walk down the path, melting every frozen thing until we * reach f. Keep wlocks so melted files are not frozen while we walk. * /active is special, because it's only frozen temporarily while * creating a frozen version of the tree. Instead of melting it, * we should just wait for it. * p[0] is / * p[1] is /active */ for(;;){ followmelted(&p->f[1], Wr); if(p->f[1]->frozen == 0) break; rwunlock(p->f[1], Wr); yield(); } /* * At loop header, parent is p->f[i-1], melted and wlocked. * At the end of the loop, p->f[i] is melted and wlocked. */ for(i = 2; i < nth; i++){ followmelted(&p->f[i], Wr); if(!p->f[i]->frozen){ rwunlock(p->f[i-1], Wr); continue; } if(catcherror()){ rwunlock(p->f[i-1], Wr); rwunlock(p->f[i], Wr); error(nil); } nf = dbdup(p->f[i]); rwlock(nf, Wr); if(catcherror()){ rwunlock(nf, Wr); mbput(nf); error(nil); } dfchdentry(p->f[i-1], p->f[i]->addr, nf->addr, Mkit); noerror(); noerror(); /* committed */ rwunlock(p->f[i-1], Wr); /* parent */ rwunlock(p->f[i], Wr); /* old frozen version */ f = p->f[i]; p->f[i] = nf; assert(f->ref > 1); mbput(f); /* ref from path */ if(!catcherror()){ dbput(f, f->type, f->addr); /* p->f[i] ref from disk */ noerror(); } } return p; }
int main(int argc, char **argv) { Pf *pf; char *orbname, *dbname, *expr, *table, *rowtemp, verbose; int orb, totalrecords, records, record; Dbptr db, dbinput, dbscratch; elog_init(argc,argv); /* initialize variables */ verbose = 0; expr = NULL; loop = 0; record = 0; totalrecords = 0; rowtemp = malloc(ROW_MAX_LENGTH); if (rowtemp == NULL) die(1,"malloc() error.\n"); /* read in command line options */ { int c; unsigned char errflg = 0; while (( c = getopt( argc, argv, "vl:s:")) != -1) switch (c) { case 'l': loop = atoi(optarg); sigset(SIGINT,done); break; case 's': expr = optarg; break; case 'v': verbose = 1; break; case '?': errflg++; } if ( (argc - optind) != 3 ) errflg++; if (errflg) { elog_die(0,"usage: %s [-v] [-s subset] [-l delay] db table orb\n",argv[0]); } dbname = argv[optind++]; table = argv[optind++]; orbname= argv[optind++]; } /* start our work */ if ( dbopen ( dbname, "r+", &dbinput ) == dbINVALID ) { elog_die(1,"Couldn't open input database, \"%s\".\n",dbname); } dbinput = dblookup( dbinput, 0, table, 0, 0); dbscratch = dblookup( dbinput, 0, 0, 0, "dbSCRATCH"); if ( dbinput.table == dbINVALID ) { elog_die(1,"Couldn't lookup the table \"%s\" in the database \"%s\".\n", table,dbname); } if ( dbscratch.record == dbINVALID ) { elog_die(1,"Couldn't lookup the scratch record in the database \"%s\".\n", dbname); } if ( dbquery( dbinput, dbRECORD_COUNT, &records) < 0 ) { elog_die(1,"dbquery dbRECORD_COUNT failed.\n"); } orb = orbopen( orbname, "w&" ); if ( orb == -1 ) { elog_die(1,"Couldn't open the orb, \"%s\".\n",orbname); } if (loop == 0) loop = -1; while (loop) { db = dbinput; if ( expr != NULL) db = dbsubset( db, expr, 0 ); db.record = record; if ( dbquery( db, dbRECORD_COUNT, &records) < 0 ) elog_die(1,"dbquery dbRECORD_COUNT failed.\n"); for (;db.record<records;db.record++) { if (verbose) printf("writing record %d to orb...\n",db.record); /* copy the row from what could be a view into the scratch record of the input database, so as to avoid putting a view on the ORB */ if ( dbget( db, rowtemp) == dbINVALID ) die(1,"dbget error.\n"); if ( dbput( dbscratch, rowtemp) == dbINVALID ) die(1,"dbput error.\n"); if ( db2orbpkt( dbscratch, orb ) < 0 ) { complain ( 0, "Couldn't write record #%d to %s.\n", db.record, orbname); } totalrecords++; } record = db.record; /* if a subset expression was supplied, then we're working with a view, which should be freed. */ if ( expr != NULL) dbfree(db); if ( loop == -1 ) loop = 0; sleep(loop); } printf("posted %d records from database %s to orb %s.\n",totalrecords,dbname,orbname); dbclose(dbinput); orbclose(orb); free(rowtemp); }