123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473(*
* Copyright (C) Citrix Systems Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*)letdebugfmt=Logging.debug"store"fmtopenJunkexceptionAlready_existsofstringmoduleNode=structtypet={name:Symbol.t;creator:int;perms:Xs_protocol.ACL.t;value:string;children:tlist;}letcreate_name_creator_perms_value={name=Symbol.of_string_name;creator=_creator;perms=_perms;value=_value;children=[];}letget_creatornode=node.creatorletset_valuenodenvalue=ifnode.value=nvaluethennodeelse{nodewithvalue=nvalue}letset_permsnodenperms={nodewithperms=nperms}letget_permsnode=node.permsletadd_childnodechild={nodewithchildren=child::node.children}letexistsnodechildname=letchildname=Symbol.of_stringchildnameinList.exists(funn->n.name=childname)node.childrenletfindnodechildname=letchildname=Symbol.of_stringchildnameinList.find(funn->n.name=childname)node.childrenletreplace_childnodechildnchild=(* this is the on-steroid version of the filter one-replace one *)letrecreplace_one_in_listl=matchlwith|[]->[]|h::tlwhenh.name=child.name->nchild::tl|h::tl->h::replace_one_in_listtlin{nodewithchildren=(replace_one_in_listnode.children)}letdel_childnamenodechildname=letsym=Symbol.of_stringchildnameinletrecdelete_one_in_listl=matchlwith|[]->raiseNot_found|h::tlwhenh.name=sym->tl|h::tl->h::delete_one_in_listtlin{nodewithchildren=(delete_one_in_listnode.children)}letdel_all_childrennode={nodewithchildren=[]}letrecrecursefctnode=fctnode;List.iter(recursefct)node.childrenendletchar_is_validc=(c>='a'&&c<='z')||(c>='A'&&c<='Z')||(c>='0'&&c<='9')||c='_'||c='-'||c='@'letname_is_validname=name<>""&&String.fold_left(funaccuc->accu&&char_is_validc)truenameletis_valid=List.for_allname_is_validtypepath=stringlistletpath_of_string=function|"/"->[]|path->ifString.lengthpath>1024theninvalid_arg"paths larger than 1024 bytes are invalid";beginmatchString.split'/'pathwith|""::path->ifnot(is_validpath)theninvalid_arg"valid paths contain only ([a-z]|[A-Z]|[0-9]|-|_|@])+";path|_->invalid_arg"valid paths have a /-prefix"endletpath_to_stringpath=String.concat"/"(""::path)moduleName=structtypet=|IntroduceDomain|ReleaseDomain|Absoluteofpath|Relativeofpathletis_relative=function|Relative_->true|_->falseletmake_absolutetpath=matchtwith|Relativep->Absolute(path_of_stringpath@p)|x->xletintroduceDomain=IntroduceDomainletreleaseDomain=ReleaseDomainletof_string=function|"@introduceDomain"->IntroduceDomain|"@releaseDomain"->ReleaseDomain|""->invalid_arg"zero-length paths are invalid";|pathwhenpath.[0]<>'/'->ifString.lengthpath>1024theninvalid_arg"paths larger than 1024 bytes are invalid";letpath=String.split'/'pathinifnot(is_validpath)theninvalid_arg"valid paths contain only ([a-z]|[A-Z]|[0-9]|-|_|@])+";Relativepath|path->Absolute(path_of_stringpath)letto_string=function|IntroduceDomain->"@introduceDomain"|ReleaseDomain->"@releaseDomain"|Absolutepath->path_to_stringpath|Relativepath->String.concat"/"pathletto_key=function|IntroduceDomain->["@introduceDomain"]|ReleaseDomain->["@releaseDomain"]|Absolutep->""::p|Relativep->""::pendmodulePath=structtypet=stringlistexceptionDoesnt_existofstringletgetdomainpathdomid=["local";"domain";Printf.sprintf"%u"domid]letcreatepathconnection_path=letopenNameinmatchof_stringpathwith|Absolutepath->path|Relativex->connection_path@x|_->invalid_arg(Printf.sprintf"invalid path: %s"path)letto_namex=Name.Absolutexletto_string=path_to_stringletto_string_listx=xletof_string_listx=xletdoesnt_existt=raise(Doesnt_exist(to_stringt))letget_parentt:t=matchtwith|[]->t|t->List.rev(List.tl(List.revt))letmake_relativebaset=letopenNameinmatchtwith|IntroduceDomain|ReleaseDomain|Relative_->t|Absolutet->(* base should be a prefix of t *)letrecfxy=matchx,ywith|x::xs,y::yswhenx=y->fxsys|[],y->Relativey|_,_->Absolutetinfbasetletlist_tl_multinl=letrecdo_tlix=ifi=0thenxelsedo_tl(i-1)(List.tlx)indo_tlnl(* string utils *)letget_hierarchypath=letl=List.lengthpathinletrevpath=List.revpathinletrecsubi=letx=List.rev(list_tl_multi(l-i)revpath)inifi=lthen[x]elsex::sub(i+1)insub0letget_common_prefixp1p2=letreccomparel1l2=matchl1,l2with|h1::tl1,h2::tl2->ifh1=h2thenh1::(comparetl1tl2)else[]|_,[]|[],_->(* if l1 or l2 is empty, we found the equal part already *)[]incomparep1p2letreclookup_modifynodepathfct=matchpathwith|[]->raiseNot_found|h::[]->fctnodeh|h::l->let(n,c)=ifnot(Node.existsnodeh)thenraise(Doesnt_existh)else(node,Node.findnodeh)inletnc=lookup_modifyclfctinNode.replace_childncncletapply_modifyrnodepathfct=lookup_modifyrnodepathfctletset_nodernodepathnnode=ifpath=[]thennnodeelseletset_nodenodename=tryletent=Node.findnodenameinNode.replace_childnodeentnnodewithNot_found->Node.add_childnodennodeinapply_modifyrnodepathset_node(* read | ls | getperms use this *)letreclookupnodepathfct=matchpathwith|[]->raiseNot_found|h::[]->fctnodeh|h::l->letcnode=Node.findnodehinlookupcnodelfctletapplyrnodepathfct=lookuprnodepathfctendtypet={mutablestat_transaction_coalesce:int;mutablestat_transaction_abort:int;mutableroot:Node.t;mutablequota:Quota.t;}letset_rootstoreroot=debug"Updating root of store";store.root<-rootletset_quotastorequota=store.quota<-quota(* modifying functions *)letpath_mkdirstorecreatorpermpath=letdo_mkdirnodename=tryletent=Node.findnodenameinPerms.checkpermPerms.WRITEent.Node.perms;raise(Already_exists(Path.to_stringpath))withNot_found->Perms.checkpermPerms.WRITEnode.Node.perms;Node.add_childnode(Node.createnamecreatornode.Node.perms"")inifpath=[]thenstore.rootelsePath.apply_modifystore.rootpathdo_mkdirletpath_writestorecreatorpermpathvalue=letnode_created=reffalseinletdo_writenodename=tryletent=Node.findnodenameinPerms.checkpermPerms.WRITEent.Node.perms;letnent=Node.set_valueentvalueinNode.replace_childnodeentnentwithNot_found->node_created:=true;Perms.checkpermPerms.WRITEnode.Node.perms;Node.add_childnode(Node.createnamecreatornode.Node.permsvalue)inifpath=[]then(Perms.checkpermPerms.WRITEstore.root.Node.perms;Node.set_valuestore.rootvalue,false)elsePath.apply_modifystore.rootpathdo_write,!node_createdletpath_rmstorepermpath=letdo_rmnodename=tryletent=Node.findnodenameinPerms.checkpermPerms.WRITEent.Node.perms;Node.del_childnamenodenamewithNot_found->Path.doesnt_existpathinifpath=[]thenNode.del_all_childrenstore.rootelsePath.apply_modifystore.rootpathdo_rmletpath_setpermsstorepermpathperms=ifpath=[]thenNode.set_permsstore.rootpermselseletdo_setpermsnodename=letc=Node.findnodenameinPerms.checkpermPerms.CHANGE_ACLc.Node.perms;Perms.checkpermPerms.WRITEc.Node.perms;letnc=Node.set_permscpermsinNode.replace_childnodecncinPath.apply_modifystore.rootpathdo_setperms(* accessing functions *)letlookupnodepath=letreclookup_getnodepath=matchpathwith|[]->raiseNot_found|h::[]->(tryNode.findnodehwithNot_found->Path.doesnt_existpath)|h::l->letcnode=Node.findnodehinlookup_getcnodelinifpath=[]thenSomenodeelse(trySome(lookup_getnodepath)withPath.Doesnt_exist_->None)letreadstorepermpath=tryletdo_readnodename=letent=Node.findnodenameinPerms.checkpermPerms.READent.Node.perms;ent.Node.valueinifpath=[]then(letent=store.rootinPerms.checkpermPerms.READent.Node.perms;ent.Node.value)elsePath.applystore.rootpathdo_readwith|Not_found->Path.doesnt_existpathletlsstorepermpath=tryletchildren=ifpath=[]thenstore.root.Node.childrenelseletdo_lsnodename=letcnode=tryNode.findnodenamewithNot_found->Path.doesnt_existpathinPerms.checkpermPerms.READcnode.Node.perms;cnode.Node.childreninPath.applystore.rootpathdo_lsinList.rev(List.map(funn->Symbol.to_stringn.Node.name)children)with|Not_found->Path.doesnt_existpathletgetpermsstorepermpath=tryifpath=[]thenstore.root.Node.permselseletfctnname=letc=Node.findnnameinPerms.checkpermPerms.READc.Node.perms;c.Node.permsinPath.applystore.rootpathfctwith|Not_found->Path.doesnt_existpathletexistsstorepath=ifpath=[]thentrueelsetryletcheck_existnodename=ignore(Node.findnodename);trueinPath.applystore.rootpathcheck_existwithNot_found->false(* modifying functions with quota udpate *)letset_nodestorepathnodeorig_quotamod_quota=letroot=Path.set_nodestore.rootpathnodeinstore.root<-root;Quota.mergeorig_quotamod_quotastore.quotaletwritestorecreatorpermpathvalue=Quota.checkstore.quotacreator(String.lengthvalue);letroot,node_created=path_writestorecreatorpermpathvalueinifnode_createdthenQuota.incrstore.quotacreator;store.root<-rootletmkdirstorecreatorpermpath=tryletroot=path_mkdirstorecreatorpermpathinQuota.incrstore.quotacreator;store.root<-rootwithAlready_exists_->()letrmstorepermpath=(* If the parent node doesn't exist then fail *)letparent=Path.get_parentpathinifnot(existsstoreparent)thenPath.doesnt_existparent;tryletrmed_node=lookupstore.rootpathinmatchrmed_nodewith|None->()|Somenodewhennode=store.root->invalid_arg"removing the root node is forbidden"|Somermed_node->store.root<-path_rmstorepermpath;Node.recurse(funnode->Quota.decrstore.quota(Node.get_creatornode))rmed_nodewith|Not_found->Path.doesnt_existpathletsetpermsstorepermpathnperms=trymatchlookupstore.rootpathwith|None->Path.doesnt_existpath|Some_->store.root<-path_setpermsstorepermpathnpermswith|Not_found->Path.doesnt_existpathletcreate()={stat_transaction_coalesce=0;stat_transaction_abort=0;root=Node.create""0(Xs_protocol.ACL.({owner=0;other=NONE;acl=[]}))"";quota=Quota.create();}letcopystore={stat_transaction_coalesce=store.stat_transaction_coalesce;stat_transaction_abort=store.stat_transaction_abort;root=store.root;quota=Quota.copystore.quota;}letmark_symbolsstore=Node.recurse(funnode->Symbol.mark_as_usednode.Node.name)store.root