#include "headers.h" typedef struct SmbSharedFileEntry SmbSharedFileEntry; struct SmbSharedFileEntry { SmbSharedFile; Ref; SmbSharedFileEntry *next; }; static struct { QLock; SmbSharedFileEntry *list; } sharedfiletable; typedef struct SmbLockListEntry SmbLockListEntry; struct SmbLockListEntry { SmbLock; SmbLockListEntry *next; }; struct SmbLockList { SmbLockListEntry *head; }; static int lockconflict(SmbLock *l1, SmbLock *l2) { return l1->base < l2->limit && l2->base < l1->limit; } static int lockorder(SmbLock *l1, SmbLock *l2) { if (l1->base < l2->base) return -1; if (l1->base > l2->base) return 1; if (l1->limit > l2->limit) return -1; if (l1->limit < l2->limit) return 1; return 0; } static void locklistfree(SmbLockList **llp) { SmbLockList *ll = *llp; if (ll) { while (ll->head) { SmbLockListEntry *next = ll->head->next; free(ll->head); ll->head = next; } free(ll); *llp = nil; } } int smbsharedfilelock(SmbSharedFile *sf, SmbSession *s, ushort pid, vlong base, vlong limit) { SmbLockListEntry smblock; SmbLockListEntry *l, *nl, **lp; smblock.s = s; smblock.pid = pid; smblock.base = base; smblock.limit = limit; if (sf->locklist) { for (l = sf->locklist->head; l; l = l->next) if (lockconflict(l, &smblock)) { smblogprintif(smbglobals.log.locks, "smbsharedfilelock: lock [%lld, %lld) failed because conflicts with [%lld, %lld)\n", base, limit, l->base, l->limit); return 0; } } if (sf->locklist == nil) sf->locklist = smbemallocz(sizeof(SmbLockList), 1); for (lp = &sf->locklist->head; (l = *lp) != nil; lp = &l->next) if (lockorder(&smblock, l) <= 0) break; smblogprintif(smbglobals.log.locks, "smbsharedfilelock: lock [%lld, %lld) succeeded\n", base, limit); nl = smbemalloc(sizeof(*nl)); *nl = smblock; nl->next = *lp; *lp = nl; //{ // smblogprintif(smbglobals.log.locks,"smbsharedfilelock: list\n"); // for (l = sf->locklist->head; l; l = l->next) // smblogprintif(smbglobals.log.locks, "smbsharedfilelock: [%lld, %lld)\n", l->base, l->limit); //} return 1; } int smbsharedfileunlock(SmbSharedFile *sf, SmbSession *s, ushort pid, vlong base, vlong limit) { SmbLockListEntry smblock; SmbLockListEntry *l, **lp; smblock.s = s; smblock.pid = pid; smblock.base = base; smblock.limit = limit; if (sf->locklist == nil) goto failed; for (lp = &sf->locklist->head; (l = *lp) != nil; lp = &l->next) { if (l->s != s || l->pid != pid) continue; switch (lockorder(&smblock, l)) { case 0: *lp = l->next; free(l); smblogprintif(smbglobals.log.locks, "smbsharedfilelock: unlock [%lld, %lld) succeeded\n", base, limit); return 1; case -1: goto failed; } } failed: smblogprintif(smbglobals.log.locks, "smbsharedfilelock: unlock [%lld, %lld) failed\n", base, limit); return 0; } static int p9denied(int p9mode, int share) { //smblogprint(-1, "p9denied(%d, %d)\n", p9mode, share); if (share == SMB_OPEN_MODE_SHARE_EXCLUSIVE) return 1; switch (p9mode & 3) { case OREAD: case OEXEC: if (share == SMB_OPEN_MODE_SHARE_DENY_READOREXEC) return 1; break; case OWRITE: if (share == SMB_OPEN_MODE_SHARE_DENY_WRITE) return 1; break; case ORDWR: if (share != SMB_OPEN_MODE_SHARE_DENY_NONE) return 1; break; } return 0; } static void sharesplit(int share, int *denyread, int *denywrite) { switch (share) { case SMB_OPEN_MODE_SHARE_EXCLUSIVE: *denyread = 1; *denywrite = 1; break; case SMB_OPEN_MODE_SHARE_DENY_READOREXEC: *denyread = 1; *denywrite = 0; break; case SMB_OPEN_MODE_SHARE_DENY_WRITE: *denywrite = 0; *denywrite = 1; break; default: *denyread = 0; *denywrite = 0; } } static int sharemake(int denyread, int denywrite) { if (denyread) if (denywrite) return SMB_OPEN_MODE_SHARE_EXCLUSIVE; else return SMB_OPEN_MODE_SHARE_DENY_READOREXEC; else if (denywrite) return SMB_OPEN_MODE_SHARE_DENY_WRITE; else return SMB_OPEN_MODE_SHARE_DENY_NONE; } static ushort sharesubtract(int share1, int share2) { int dr1, dw1; int dr2, dw2; sharesplit(share1, &dr1, &dw1); sharesplit(share2, &dr2, &dw2); if (dw2) dw1 = 0; if (dr2) dr1 = 0; return sharemake(dr1, dw1); } static int shareadd(int share1, int share2) { int dr1, dw1; int dr2, dw2; sharesplit(share1, &dr1, &dw1); sharesplit(share2, &dr2, &dw2); if (dw2) dw1 = 1; if (dr2) dr1 = 1; return sharemake(dr1, dw1); } SmbSharedFile * smbsharedfileget(Dir *d, int p9mode, int *sharep) { SmbSharedFileEntry *sfe; qlock(&sharedfiletable); for (sfe = sharedfiletable.list; sfe; sfe = sfe->next) { if (sfe->type == d->type && sfe->dev == d->dev && sfe->path == d->qid.path) { if (p9denied(p9mode, sfe->share)) { qunlock(&sharedfiletable); return nil; } *sharep = sharesubtract(*sharep, sfe->share); sfe->share = shareadd(sfe->share, *sharep); sfe->ref++; goto done; } } sfe = smbemallocz(sizeof(SmbSharedFileEntry), 1); sfe->type = d->type; sfe->dev = d->dev; sfe->path = d->qid.path; // sfe->name = smbestrdup(name); sfe->ref = 1; sfe->share = *sharep; sfe->next = sharedfiletable.list; sharedfiletable.list = sfe; done: smblogprintif(smbglobals.log.sharedfiles, "smbsharedfileget: ref %d share %d\n", sfe->ref, sfe->share); qunlock(&sharedfiletable); return sfe; } void smbsharedfileput(SmbFile *f, SmbSharedFile *sf, int share) { SmbSharedFileEntry *sfe, **sfep; qlock(&sharedfiletable); for (sfep = &sharedfiletable.list; (sfe = *sfep) != nil; sfep = &sfe->next) { if (sfe == sf) { sfe->ref--; if (sfe->ref == 0) { *sfep = sfe->next; if (sfe->deleteonclose && f) smbremovefile(f->t, nil, f->name); smblogprintif(smbglobals.log.sharedfiles, "smbsharedfileput: removed\n"); locklistfree(&sfe->locklist); free(sfe); } else { sfe->share = sharesubtract(sfe->share, share); smblogprintif(smbglobals.log.sharedfiles, "smbsharedfileput: ref %d share %d\n", sfe->ref, sfe->share); } break; } } qunlock(&sharedfiletable); }