PartitionsLib.Mod 139 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156
  1. MODULE PartitionsLib; (** AUTHOR "staubesv"; PURPOSE "Partitioning and formatting tool for N2KFS and AosFS. "; *)
  2. (**
  3. * Overview:
  4. *
  5. * Operation: Base object for generic disk operation
  6. * OperationManager Manages operation objects
  7. * DiskModel: Internal represenation of currenly present devices (and their partition layout). Provides locking mechanism for
  8. * disk operations.
  9. *
  10. * Note: Most of the actual disk operation implementation has been taken from Partitions.Mod from "pjm".
  11. *
  12. * History:
  13. *
  14. * 05.08.2005 Cleanup (staubesv)
  15. * 25.11.2005 Integrated windows 2000 workaround from Partitions.Mod, fixed CheckDisk operation reporting negative speeds,
  16. * Operation uses Text instead of StringWriter (staubesv)
  17. * 07.12.2005 Fixed InitOBL (did not use disk geometry correctly -> OBL failed when booting), fixed progress report of FileToPartition (staubesv)
  18. * 09.12.2005 Fixed Operation.SetStart deadlock (staubesv)
  19. * 12.12.2005 Operation object uses exception handling to release ressources -> more stable, adapted operations (staubesv)
  20. * 12.12.2005 Implemented Reader/Writer locks (staubesv)
  21. * 15.12.2005 DiskModel.Update now also calls DiskModel.OnChanged (staubesv)
  22. * 16.12.2005 Operation.state.min/cur/max type change to HUGEINT to prevent overflows (staubesv)
  23. * 19.12.2005 Fixed DeletePartition/CreatePartition (staubesv)
  24. * 06.01.2006 Fixed nn fsRes handling (staubesv)
  25. * 17.01.2006 Made output more appropriate for Partitions front-end, fixed Configuration.ParseConfig (staubesv)
  26. * 24.02.2006 PartitionToFile & FileToPartition not dependent on device blocksize anymore (staubesv)
  27. * 09.03.2006 Improved DiskModel.GetFS and renamed it to DiskModel.AssignFileSystems (staubesv)
  28. * 18.03.2006 Small changes/cleanup in ShowBlocks, added Operation.SetParent, added InstallBootManager (staubesv)
  29. * 12.08.2006 GetDriveNum: treat partitioned devices as non-removable
  30. * 02.08.2007 DiskModel.GetDisk: Fixed NIL trap if no disks are available (staubesv)
  31. *)
  32. (*
  33. * OBL variables (in boot block)
  34. *
  35. * ofs size description
  36. * 00 03 ?,?,?
  37. * 03 06 "OBERON"
  38. * 09 01 ?
  39. * 0A 01 flag (if 0, start config string editor, otherwise, bits 0-4 tested with shift bits from BIOS)
  40. * 0B 02 ?
  41. * 0D 01 ?
  42. * 0E 02 reserved blocks
  43. * 10 01 config table size in blocks
  44. * 11 02 ?
  45. * 13 02 total blocks (or 0)
  46. * 15 01 ?
  47. * 16 02 ?
  48. * 18 02 blocks per track
  49. * 1A 02 heads
  50. * 1C 04 boot block number
  51. * 20 04 total blocks (if 13 is 0)
  52. * 24 01 drive number (0, 1 for floppy, 80H, 81H, ... for hard disk)
  53. *
  54. * AosFS Table Format (in boot block)
  55. *
  56. * New (post 14.03.00)
  57. * 1F0H 4 fileSystemOfs (in 512-byte blocks, relative to this block)
  58. * 1F4H 4 fileSystemSize (in sectors, aka volume blocks)
  59. * 1F8H 4 id = 21534F41H ("AOS!")
  60. * 1FCH 1 version = 1X
  61. * 1FDH 1 sectorSizeLog2 = 12 (4096)
  62. * 1FEH 1 bootID0 = 055X
  63. * 1FFH 1 bootID1 = 0AAX
  64. *
  65. * Old (pre 14.03.00)
  66. * 1E0H 4 fileSystemOfs (in blocks, relative to this block)
  67. * 1E4H 4 fileSystemSize (in sectors)
  68. * 1E8H 16 volumeName (0X-terminated)
  69. * 1F8H 4 id = 5245424FH ("OBER")
  70. * 1FCH 1 version = 1X
  71. * 1FDH 1 sectorSizeLog2 = 12 (4096)
  72. * 1FEH 1 bootID0 = 055X
  73. * 1FFH 1 bootID1 = 0AAX
  74. *
  75. * Partition layout (N2KFS and AosFS overlayed)
  76. *
  77. * block description
  78. * 0..3 OBL.Bin (4 blocks)
  79. * <-- BootLoaderSize
  80. * 4..7 Config table (size from 10H)
  81. * <-- start of BootFile
  82. * <-- reserved blocks pointer (from 0EH)
  83. * x.. N2KFS
  84. * <-- fileSystemOfs pointer (from 1F0H)
  85. * y.. AosFS
  86. *)
  87. IMPORT KernelLog, Kernel, Modules, Commands, AosDisks := Disks, Files, Dates, Strings, Plugins, Streams, Objects,
  88. WMGraphics, WMEvents, DiskVolumes, OldDiskVolumes, FATVolumes, ISO9660Volumes, Texts, TextUtilities;
  89. CONST
  90. (* Result codes *)
  91. Ok* = 0;
  92. (* Operation status *)
  93. StatusRunning* = 1;
  94. StatusAborted* = 2;
  95. StatusWaiting* = 3;
  96. StatusFinished* = 5;
  97. StatusError* = 4;
  98. (* Disk model lock types. More restrictive lock types must have lower numbers! *)
  99. WriterLock* = 0; (* default *)
  100. ReaderLock* = 1;
  101. (* DetectFS return codes *)
  102. UnknownFS* = 0;
  103. NativeFS* = 1;
  104. OldAosFS32* = 2;
  105. AosFS32* = 3;
  106. FatFS* = 4;
  107. AosFS128* = 5;
  108. Trace = {};
  109. TraceGeometry = {1};
  110. TraceGetFS = {2};
  111. TraceLocks = {3};
  112. Debug = TRUE;
  113. ShowReserved = FALSE; (* Show reserved space in partitions *)
  114. DateTimeFormat = "yyyy.mm.dd hh:nn:ss";
  115. (* Default name for boot file *)
  116. BootFileName = "IDE.Bin";
  117. MaxBootLoaderSize=10; (* blocks *)
  118. BS* = 512;
  119. MinPartSize = 64; (* absolute minimum number of sectors in Oberon partition *)
  120. N2KSS = 2048;
  121. N2KBPS = N2KSS DIV BS;
  122. N2KDirMark = LONGINT(9B1EA38DH);
  123. AosSSLog2 = 12;
  124. AosSS = ASH(1, AosSSLog2); (* Sector Size *)
  125. AosBPS = AosSS DIV BS;
  126. AosSF = 29;
  127. AosSTS = 128;
  128. AosXS = AosSS DIV 4;
  129. AosHS = 568;
  130. AosDirMark = LONGINT(9B1EA38DH);
  131. AosType = 76;
  132. NativeType1 = 79;
  133. NativeType2 = 80;
  134. FSID = 21534F41H;
  135. FSID0 = 5245424FH;
  136. FSIDOBL = 44494449H;
  137. FSVer = 2;
  138. FSRes = 640*1024 DIV BS; (* default blocks reserved for boot file *)
  139. MaxConfig* = 2000; (* less than tsize*BS *)
  140. MaxConfigString* = 4096;
  141. WholeDisk = 256;
  142. FreeSpace = -1;
  143. ReservedSpace = -2;
  144. NoSpaceAvailable = 9001;
  145. CoreMismatch = 9002; (* core file on disk does not match *)
  146. CoreChecksumError = 9003; (* core file checksum mismatch *)
  147. DisketteLimit = 2880; (* if device has <= this many sectors, assume it is a diskette without partition table *)
  148. (* Offsets of slot in partition table of MBR/EPBR *)
  149. Slot1 = 1BEH;
  150. Slot2 = 1CEH;
  151. Slot3 = 1DEH;
  152. Slot4 = 1EEH;
  153. MaxStringLength = 1024;
  154. TYPE
  155. Block* = ARRAY BS OF CHAR;
  156. TYPE
  157. Disk* = RECORD
  158. device* : AosDisks.Device;
  159. table* : AosDisks.PartitionTable; (* extended copy of device.table *)
  160. size*: LONGINT; res*: WORD; (* device.GetSize(size, res); Size is valid only when res = AosDisks.Ok *)
  161. geo* : AosDisks.GetGeometryMsg; (* device.Handle(geo, res); geo is valid only when res = AosDisks.Ok *)
  162. gres* : WORD;
  163. fs* : POINTER TO ARRAY OF Files.FileSystem;
  164. isDiskette* : BOOLEAN; (* is this a floppy drive diskette? *)
  165. END;
  166. Disks* = POINTER TO ARRAY OF Disk;
  167. Selection* = RECORD
  168. disk* : Disk;
  169. partition* : LONGINT;
  170. END;
  171. TYPE
  172. RegionLock = POINTER TO RECORD
  173. device : AosDisks.Device;
  174. partition : AosDisks.Partition; (* we consider only partition.start & partition.size *)
  175. type : LONGINT; (* lock type: WriterLock | ReaderLock *)
  176. nbrOfReaders : LONGINT; (* ReaderLock only: How many readers hold the lock? *)
  177. next : RegionLock;
  178. END;
  179. TYPE
  180. DisksModel* = OBJECT
  181. VAR
  182. lockedBy : ANY;
  183. lockLevel : LONGINT;
  184. onChanged* : WMEvents.EventSource; (** does not hold the lock, if called *)
  185. devTable : Plugins.Table;
  186. disks- : Disks;
  187. usedDisks : RegionLock; (* note: synchronization with disks not needed *)
  188. (* IF check = TRUE, only partitions with the AosDisks.Valid flags set are returned *)
  189. PROCEDURE GetDisk*(CONST devpart : ARRAY OF CHAR; VAR selection : Selection; check : BOOLEAN) : BOOLEAN;
  190. VAR devname, partStr : ARRAY 32 OF CHAR; i, j : LONGINT;
  191. BEGIN
  192. IF disks # NIL THEN
  193. i := 0;
  194. LOOP (* get device name *)
  195. devname[i] := devpart[i]; (* at least one character before "#" *)
  196. INC(i); IF (i >= LEN(devpart)) OR (i >= LEN(devname)) OR (devpart[i]="#") THEN EXIT END;
  197. END;
  198. IF (i < LEN(devpart)) & (devpart[i]="#") THEN
  199. devname[i] := 0X; INC(i);
  200. j := 0;
  201. LOOP (* get partition number *)
  202. partStr[j] := devpart[i];
  203. INC(i); INC(j); IF (i >= LEN(devpart)) OR (j >= LEN(partStr)) OR (devpart[i]=0X) THEN EXIT END;
  204. END;
  205. IF (i < LEN(devpart)) THEN
  206. Strings.StrToInt(partStr, selection.partition);
  207. Acquire; (* lock disks[] *)
  208. i := 0;
  209. LOOP (* get disk record *)
  210. IF (disks[i].device # NIL) & Strings.Match(devname, disks[i].device.name) THEN EXIT END;
  211. INC(i); IF i >= LEN(disks) THEN EXIT END;
  212. END;
  213. Release;
  214. IF (i < LEN(disks)) THEN (* disk record found *)
  215. IF (disks[i].device # NIL) & (disks[i].table # NIL) & (disks[i].device.table # NIL) & (selection.partition >= 0) THEN
  216. IF (check & ((disks[i].device.table # NIL) & (selection.partition < LEN(disks[i].device.table)))) OR (~check & (selection.partition < LEN(disks[i].table))) THEN
  217. selection.disk := disks[i];
  218. RETURN TRUE;
  219. END;
  220. END;
  221. ELSIF Debug THEN KernelLog.String("PartitionsLib.diskModel.GetDisk: "); KernelLog.String(devpart); KernelLog.String(" not found"); KernelLog.Ln;
  222. END;
  223. ELSIF Debug THEN KernelLog.String("PartitionsLib.diskModel.GetDisk: "); KernelLog.String(devpart); KernelLog.String(" : ParseError"); KernelLog.Ln;
  224. END;
  225. ELSIF Debug THEN KernelLog.String("PartitionsLib.diskModel.GetDisk: "); KernelLog.String(devpart); KernelLog.String(" : ParseError"); KernelLog.Ln;
  226. END;
  227. ELSIF Debug THEN KernelLog.String("PartitionsLib.diskModel.GetDisk: No disks available"); KernelLog.Ln;
  228. END;
  229. RETURN FALSE;
  230. END GetDisk;
  231. (* Lock a partition. Returns TRUE if locked succeded, FALSE otherwise *)
  232. PROCEDURE AcquirePartition*(disk : Disk; partition : LONGINT; locktype : LONGINT) : BOOLEAN;
  233. VAR table : AosDisks.PartitionTable; lock, temp : RegionLock; getReaderLock : BOOLEAN;
  234. BEGIN {EXCLUSIVE}
  235. ASSERT(disk.device # NIL);
  236. (* check whether region is already locked *)
  237. NEW(table, 2);
  238. table[0] := disk.table[partition];
  239. temp := usedDisks; getReaderLock := FALSE;
  240. LOOP
  241. IF temp.next = NIL THEN EXIT END; (* region is not locked *)
  242. IF temp.next.device = disk.device THEN (* there are some operations locking region on the device *)
  243. table[1] := temp.next.partition;
  244. IF PartitionsOverlap(table, 0, 1) THEN
  245. IF (locktype = WriterLock) OR (temp.next.type = WriterLock) THEN
  246. getReaderLock := FALSE;
  247. EXIT (* region is locked ! *)
  248. ELSE
  249. getReaderLock := TRUE;
  250. END;
  251. END;
  252. END;
  253. temp := temp.next;
  254. END;
  255. IF temp.next = NIL THEN
  256. IF getReaderLock THEN (* we can acquire a reader lock only *)
  257. temp := usedDisks;
  258. WHILE temp.next # NIL DO
  259. IF (temp.next.device = disk.device) & PartitionsOverlap(table, 0, 1) THEN INC(temp.next.nbrOfReaders); END;
  260. temp := temp.next;
  261. END;
  262. ELSE (* the region has not yet been locked... lock it *)
  263. NEW(lock); lock.device := disk.device; lock.partition := disk.table[partition]; lock.type := locktype;
  264. IF locktype = ReaderLock THEN lock.nbrOfReaders := 1; END;
  265. temp.next := lock;
  266. END;
  267. IF Trace * TraceLocks # {} THEN KernelLog.String("LOCK GRANTED: "); ShowLocks; END;
  268. RETURN TRUE;
  269. END;
  270. IF Trace * TraceLocks # {} THEN KernelLog.String("LOCK DENIED: "); ShowLocks; END;
  271. RETURN FALSE;
  272. END AcquirePartition;
  273. (* Release a partition. *)
  274. PROCEDURE ReleasePartition*(disk : Disk; partition : LONGINT);
  275. VAR temp : RegionLock;
  276. BEGIN {EXCLUSIVE}
  277. ASSERT(disk.device # NIL);
  278. temp := usedDisks;
  279. WHILE (temp.next # NIL) DO
  280. IF (temp.next.device = disk.device) & (temp.next.partition.start = disk.table[partition].start) &
  281. (temp.next.partition.size = disk.table[partition].size) THEN (* lock found *)
  282. IF temp.next.type = WriterLock THEN
  283. temp.next := temp.next.next;
  284. IF Trace * TraceLocks # {} THEN KernelLog.String("WRITER LOCK REMOVED: "); ShowLocks; END;
  285. RETURN;
  286. ELSE
  287. DEC(temp.next.nbrOfReaders);
  288. IF temp.next.nbrOfReaders <= 0 THEN (* release region *)
  289. temp.next := temp.next.next;
  290. ELSE
  291. temp := temp.next;
  292. END;
  293. END;
  294. ELSE
  295. temp := temp.next;
  296. END;
  297. END;
  298. IF Trace * TraceLocks # {} THEN KernelLog.String("AFTER RELEASING A LOCK: "); ShowLocks; END;
  299. END ReleasePartition;
  300. (* Show all locks currently held *)
  301. PROCEDURE ShowLocks;
  302. VAR temp : RegionLock;
  303. BEGIN
  304. IF Trace * TraceLocks # {} THEN
  305. KernelLog.String("PartitionsLib.DiskModel locks: "); KernelLog.Ln;
  306. temp := usedDisks;
  307. IF temp = NIL THEN
  308. KernelLog.String("no locks granted");
  309. ELSE
  310. WHILE temp.next # NIL DO
  311. KernelLog.String("Device: "); KernelLog.String(temp.next.device.name);
  312. KernelLog.String(", LBA start: "); KernelLog.Int(temp.next.partition.start, 0);
  313. KernelLog.String(", LBA end: "); KernelLog.Int(temp.next.partition.start + temp.next.partition.size - 1, 0);
  314. KernelLog.String(", Lock Type: ");
  315. IF temp.next.type = WriterLock THEN
  316. KernelLog.String("WriterLock");
  317. ELSE
  318. KernelLog.String("ReaderLock ["); KernelLog.Int(temp.next.nbrOfReaders, 0); KernelLog.String(" readers]");
  319. END;
  320. temp := temp.next;
  321. END;
  322. END;
  323. KernelLog.Ln;
  324. END;
  325. END ShowLocks;
  326. PROCEDURE &Init*;
  327. VAR res : WORD;
  328. BEGIN
  329. NEW(usedDisks);
  330. AosDisks.registry.GetAll(devTable);
  331. NEW(onChanged, SELF,Strings.NewString("DiskModelChanged"), NIL, NIL);
  332. lockLevel := 0;
  333. UpdateAllDisks;
  334. AosDisks.registry.AddEventHandler(DiskEventHandler, res);
  335. END Init;
  336. (** Update partition tables & file systems of all disk devices *)
  337. PROCEDURE Update*;
  338. BEGIN
  339. UpdateAllDisks;
  340. onChanged.Call(NIL);
  341. END Update;
  342. PROCEDURE UpdateAllDisks;
  343. VAR
  344. dev : AosDisks.Device;
  345. doClose : BOOLEAN;
  346. errorWriter : Streams.StringWriter;
  347. errorString : ARRAY 1024 OF CHAR;
  348. i, j : LONGINT; res: WORD;
  349. BEGIN (* caller must hold lock *)
  350. Acquire;
  351. IF devTable # NIL THEN
  352. NEW(disks, LEN(devTable));
  353. FOR i := 0 TO LEN(devTable)-1 DO
  354. dev := devTable[i] (AosDisks.Device);
  355. disks[i].device := dev;
  356. (* Hack to support diskette drives *)
  357. IF Strings.Match("Diskette0*", dev.name) THEN
  358. disks[i].isDiskette := TRUE;
  359. doClose := FALSE;
  360. IF (dev.openCount < 1) THEN
  361. dev.Open(disks[i].res); (* ignore res *)
  362. doClose := TRUE;
  363. END;
  364. END;
  365. dev.GetSize(disks[i].size, disks[i].res);
  366. IF disks[i].res = AosDisks.MediaChanged THEN dev.GetSize(disks[i].size, disks[i].res) END; (* we didn't use Open, so retry *)
  367. IF disks[i].res # AosDisks.MediaMissing THEN
  368. AosDisks.UpdatePartitionTable(dev, res);
  369. IF ((res = AosDisks.Ok) OR (res = AosDisks.DeviceInUse)) & (dev.table # NIL) THEN
  370. NEW(disks[i].fs, LEN(dev.table));
  371. (* copy partition table - the copy will be extended by FindFreeSpace *)
  372. NEW(disks[i].table, LEN(disks[i].device.table));
  373. FOR j := 0 TO LEN(disks[i].device.table)-1 DO disks[i].table[j] := disks[i].device.table[j]; END;
  374. GetGeometry(disks[i], disks[i].geo, disks[i].gres); (* calls dev.Handle(geo, res) *)
  375. IF ((res = AosDisks.DeviceInUse) OR (res = AosDisks.Ok)) & (dev.blockSize = BS) & (disks[i].geo.cyls * disks[i].geo.hds * disks[i].geo.spt > DisketteLimit) THEN
  376. NEW(errorWriter, LEN(errorString));
  377. (* possibly re-allocate table *)
  378. IF ~FindFreeSpace(errorWriter, dev, disks[i].table, disks[i].geo.spt, disks[i].geo.hds) THEN
  379. errorWriter.Get(errorString);
  380. KernelLog.String("PartitionsLib: "); KernelLog.String(errorString); KernelLog.Ln;
  381. END;
  382. END;
  383. END;
  384. AssignFileSystems(i);
  385. END;
  386. IF disks[i].isDiskette & doClose & (dev.openCount > 0) THEN dev.Close(res); (* ignore res *) END;
  387. END;
  388. ELSE
  389. disks := NIL;
  390. END;
  391. Release;
  392. END UpdateAllDisks;
  393. PROCEDURE UpdateDisk*(disk : Disk);
  394. VAR i : LONGINT;
  395. BEGIN
  396. IF disks # NIL THEN
  397. Acquire;
  398. WHILE (i < LEN(disks)) & (disks[i].device # disk.device) DO INC(i); END;
  399. IF (i < LEN(disks)) THEN (* disk found *)
  400. UpdateDiskInternal(i);
  401. ELSE
  402. IF Debug THEN KernelLog.String("PartitionsLib.diskModel.UpdateDisk: disk not found"); KernelLog.Ln; END;
  403. END;
  404. Release;
  405. onChanged.Call(NIL);
  406. ELSE
  407. IF Debug THEN KernelLog.String("PartitionsLib.diskModel.UpdateDisk: disk not found (2)"); KernelLog.Ln; END;
  408. END;
  409. END UpdateDisk;
  410. PROCEDURE UpdateDiskInternal(i : LONGINT);
  411. VAR dev : AosDisks.Device; errorWriter : Streams.StringWriter; errorString : ARRAY 1024 OF CHAR; doClose : BOOLEAN; j : LONGINT; res: WORD;
  412. BEGIN
  413. dev := disks[i].device;
  414. IF dev # NIL THEN
  415. (* Hack to support diskettes *)
  416. IF Strings.Match("Diskette0*", dev.name) THEN
  417. disks[i].isDiskette := TRUE; doClose := FALSE;
  418. IF dev.openCount < 1 THEN
  419. dev.Open(res); (* ignore res *)
  420. doClose := TRUE;
  421. END;
  422. END;
  423. dev.GetSize(disks[i].size, disks[i].res);
  424. IF disks[i].res = AosDisks.MediaChanged THEN dev.GetSize(disks[i].size, disks[i].res) END; (* we didn't use Open, so retry *)
  425. IF disks[i].res # AosDisks.MediaMissing THEN
  426. AosDisks.UpdatePartitionTable(dev, res);
  427. IF ((res = AosDisks.Ok) OR (res = AosDisks.DeviceInUse)) & (dev.table # NIL) THEN
  428. NEW(disks[i].fs, LEN(dev.table));
  429. (* copy partition table - the copy will be extended by FindFreeSpace *)
  430. NEW(disks[i].table, LEN(disks[i].device.table));
  431. FOR j := 0 TO LEN(disks[i].device.table)-1 DO disks[i].table[j] := disks[i].device.table[j]; END;
  432. GetGeometry(disks[i], disks[i].geo, disks[i].gres); (* calls dev.Handle(geo, res) *)
  433. IF ((res = AosDisks.Ok) OR (res = AosDisks.DeviceInUse)) & (dev.blockSize = BS) & (disks[i].geo.cyls * disks[i].geo.hds * disks[i].geo.spt > DisketteLimit) THEN
  434. (* possibly re-allocate table *)
  435. NEW(errorWriter, LEN(errorString));
  436. IF ~FindFreeSpace(errorWriter, dev, disks[i].table, disks[i].geo.spt, disks[i].geo.hds) THEN
  437. errorWriter.Get(errorString);
  438. KernelLog.String("PartitionsLib: "); KernelLog.String(errorString); KernelLog.Ln;
  439. END;
  440. END;
  441. END;
  442. END;
  443. AssignFileSystems(i);
  444. IF disks[i].isDiskette & doClose & (dev.openCount > 0) THEN dev.Close(res); (* ignore res *) END;
  445. END;
  446. END UpdateDiskInternal;
  447. (** acquire a read/write lock on the object *)
  448. PROCEDURE Acquire*;
  449. VAR me : ANY;
  450. BEGIN {EXCLUSIVE}
  451. me := Objects.ActiveObject();
  452. IF lockedBy = me THEN
  453. ASSERT(lockLevel # -1); (* overflow *)
  454. INC(lockLevel);
  455. ELSE
  456. AWAIT(lockedBy = NIL);
  457. lockedBy := me; lockLevel := 1
  458. END
  459. END Acquire;
  460. (** release the read/write lock on the object *)
  461. PROCEDURE Release*;
  462. BEGIN {EXCLUSIVE}
  463. ASSERT(lockedBy = Objects.ActiveObject(), 3000);
  464. DEC(lockLevel);
  465. IF lockLevel = 0 THEN lockedBy := NIL; END
  466. END Release;
  467. PROCEDURE DiskEventHandler(event : WORD; plugin : Plugins.Plugin);
  468. VAR tempTable : Plugins.Table; dev : AosDisks.Device; i, j : LONGINT;
  469. BEGIN
  470. ASSERT(plugin#NIL);
  471. dev := plugin (AosDisks.Device);
  472. Acquire;
  473. IF event = Plugins.EventAdd THEN
  474. IF devTable # NIL THEN
  475. NEW(tempTable, LEN(devTable)+1);
  476. FOR i := 0 TO LEN(devTable)-1 DO tempTable[i] := devTable[i]; END;
  477. tempTable[LEN(devTable)] := dev;
  478. devTable := tempTable;
  479. ELSE
  480. NEW(devTable, 1); devTable[0] := dev;
  481. END;
  482. ELSIF event = Plugins.EventRemove THEN
  483. IF (devTable#NIL) & (LEN(devTable)>1) THEN
  484. NEW(tempTable, LEN(devTable)-1);
  485. i := 0; j := 0;
  486. LOOP
  487. IF devTable[i]#dev THEN
  488. tempTable[j] := devTable[i];
  489. INC(j);
  490. END;
  491. INC(i);
  492. IF i >= LEN(devTable) THEN EXIT; END;
  493. END;
  494. devTable := tempTable;
  495. ELSE
  496. devTable := NIL;
  497. END;
  498. ELSE
  499. IF Debug THEN KernelLog.String("PartitionsLib.diskModel.DiskEventHandler: Wrong event"); KernelLog.Ln; END;
  500. END;
  501. UpdateAllDisks;
  502. Release;
  503. onChanged.Call(NIL);
  504. END DiskEventHandler;
  505. (* Get geometry from partition table, if possible. *)
  506. PROCEDURE GetTableGeometry(dev: AosDisks.Device; VAR hds, spt: LONGINT): BOOLEAN;
  507. VAR buf: ARRAY BS OF CHAR; res: WORD; p, hd, sec, i: LONGINT; ok: BOOLEAN;
  508. BEGIN
  509. ok := FALSE;
  510. IF dev.blockSize # BS THEN RETURN FALSE; END;
  511. dev.Transfer(AosDisks.Read, 0, 1, buf, 0, res);
  512. IF (res = AosDisks.Ok) & (buf[510] = 055X) & (buf[511] = 0AAX) & (buf[Slot1+4] = 055X) THEN (* EZDrive *)
  513. dev.Transfer(AosDisks.Read, 1, 1, buf, 0, res) (* read sector 1 *)
  514. END;
  515. IF (res = AosDisks.Ok) & (buf[510] = 055X) & (buf[511] = 0AAX) THEN (* valid partition table *)
  516. hds := -1;
  517. FOR i := 0 TO 3 DO (* find end head and sector for each valid primary partition *)
  518. p := Slot1 + 16*i;
  519. IF buf[p+4] # 0X THEN (* partition i in use *)
  520. hd := ORD(buf[p+5]); (* end head *)
  521. sec := ORD(buf[p+6]) MOD 64; (* end sector *)
  522. IF hds = -1 THEN
  523. hds := hd+1; spt := sec; ok := TRUE (* first partition found *)
  524. ELSIF (hds = hd+1) & (spt = sec) THEN
  525. (* skip *)
  526. ELSE
  527. ok := FALSE (* inconsistent table *)
  528. END
  529. END
  530. END
  531. END;
  532. IF (hds<=0) OR (spt <= 0) OR ~ok THEN hds := 0; spt := 0; ok := FALSE; END;
  533. RETURN ok
  534. END GetTableGeometry;
  535. (* Get drive geometry and adjust it. *)
  536. PROCEDURE GetGeometry(disk: Disk; VAR geo: AosDisks.GetGeometryMsg; VAR res: WORD);
  537. VAR dev : AosDisks.Device; thds, tspt, dsize: LONGINT; org: AosDisks.GetGeometryMsg;
  538. BEGIN
  539. dev := disk.device;
  540. dev.Handle(geo, res);
  541. IF res # AosDisks.Ok THEN
  542. IF Trace * TraceGeometry # {} THEN KernelLog.String("Partitions: GetGeometry result "); KernelLog.Int(res, 1); KernelLog.Ln END;
  543. IF dev.blockSize = BS THEN (* try getSize instead *)
  544. dev.GetSize(dsize, res);
  545. IF res = AosDisks.Ok THEN
  546. geo.cyls := 1; geo.hds := 1; geo.spt := dsize; (* fake it *)
  547. END
  548. END
  549. END;
  550. IF (res = AosDisks.Ok) & (dev.blockSize = BS) THEN (* adjust geometry *)
  551. org := geo; dsize := geo.cyls*geo.hds*geo.spt;
  552. IF GetTableGeometry(dev, thds, tspt) THEN (* adjust geometry to partition table *)
  553. geo.cyls := dsize DIV (thds*tspt);
  554. geo.hds := thds; geo.spt := tspt
  555. ELSIF (geo.cyls > 1024) OR (geo.hds > 255) OR (geo.spt > 63) THEN
  556. (* modify the parameters to be inside BIOS limits (for boot loader) *)
  557. (* BIOS limits: 1024 cylinders (0-1023), 255 heads (0-254), 63 sectors (1-63) (max size 8032M) *)
  558. geo.hds := 1; geo.spt := 63;
  559. REPEAT (* try 2, 4, 8, 16, 32, 64, 128 and 255 heads *)
  560. geo.hds := geo.hds*2;
  561. geo.cyls := dsize DIV (geo.hds*geo.spt)
  562. UNTIL (geo.cyls <= 1023) OR (geo.hds = 256);
  563. IF geo.hds = 256 THEN geo.hds := 255; geo.cyls := dsize DIV (geo.hds*geo.spt) END
  564. ELSE
  565. (* skip - ok *)
  566. END;
  567. IF Trace * TraceGeometry # {} THEN
  568. IF (org.cyls # geo.cyls) OR (org.hds # geo.hds) OR (org.spt # geo.spt) THEN
  569. KernelLog.String("Partitions: "); KernelLog.String(dev.name); KernelLog.Char(" ");
  570. KernelLog.Int(org.cyls, 1); KernelLog.Char("*"); KernelLog.Int(org.hds, 1); KernelLog.Char("*"); KernelLog.Int(org.spt, 1); KernelLog.Char("=");
  571. KernelLog.Int(dsize, 1); KernelLog.String(" -> "); KernelLog.Int(geo.cyls, 1); KernelLog.Char("*"); KernelLog.Int(geo.hds, 1); KernelLog.Char("*");
  572. KernelLog.Int(geo.spt, 1); KernelLog.Char("="); KernelLog.Int(geo.cyls*geo.hds*geo.spt, 1); KernelLog.Ln;
  573. END
  574. END
  575. END
  576. END GetGeometry;
  577. (* Add a free partition entry at the end (to keep partition numbers the same) *)
  578. PROCEDURE NewFree(type: LONGINT; VAR table: AosDisks.PartitionTable; start, size, ptblock: LONGINT; flags: SET);
  579. VAR j: LONGINT; p: AosDisks.Partition; new: AosDisks.PartitionTable;
  580. BEGIN
  581. p.type := type; p.start := start; p.size := size; p.flags := flags;
  582. p.ptblock := ptblock; p.ptoffset := 0; (* find free ptoffset later *)
  583. NEW(new, LEN(table)+1); j := 0;
  584. WHILE j # LEN(table) DO new[j] := table[j]; INC(j) END;
  585. new[j] := p; table := new
  586. END NewFree;
  587. PROCEDURE FindFreePrimary(VAR table: AosDisks.PartitionTable; spt, hds: LONGINT);
  588. VAR i, g, t, max, start, end, prevstart, nextstart: LONGINT;
  589. BEGIN
  590. start := spt; g := hds * spt; (* skip first track *)
  591. max := table[0].size - g; (* reserve one cylinder at end of disk *)
  592. FOR i := 1 TO LEN(table)-1 DO (* find overlapping partition, if any *)
  593. IF (AosDisks.Primary IN table[i].flags) & (table[i].start <= start) & (start < table[i].start+table[i].size) THEN
  594. start := table[i].start (* start search at this partition instead *)
  595. END
  596. END;
  597. LOOP
  598. prevstart := start; end := MAX(LONGINT);
  599. FOR i := 1 TO LEN(table)-1 DO (* find first partition start after or on start *)
  600. IF (AosDisks.Primary IN table[i].flags) & (table[i].start >= start) & (table[i].start < end) THEN
  601. end := table[i].start (* free space ends at this start position *)
  602. END
  603. END;
  604. IF end > max THEN end := max END; (* clip to end of disk *)
  605. (* {start..end-1 is free} *)
  606. IF start # spt THEN INC(start, (-start) MOD g) END; (* start on cylinder boundary (except first) *)
  607. DEC(end, end MOD g); (* end on cylinder boundary *)
  608. (* {start..end-1 is free and aligned} *)
  609. IF end-start > 0 THEN NewFree(FreeSpace, table, start, end-start, 0, {AosDisks.Primary}) END;
  610. nextstart := MAX(LONGINT);
  611. FOR i := 1 TO LEN(table)-1 DO (* find first partition end after prevstart *)
  612. IF AosDisks.Primary IN table[i].flags THEN
  613. t := table[i].start+table[i].size-1;
  614. IF (t > prevstart) & (t < nextstart) THEN nextstart := t END
  615. END
  616. END;
  617. IF nextstart = MAX(LONGINT) THEN
  618. EXIT (* no more partitions end after prevstart *)
  619. ELSE
  620. start := nextstart+1
  621. END
  622. END
  623. END FindFreePrimary;
  624. PROCEDURE FindFreeExtended(VAR table: AosDisks.PartitionTable; spt, hds: LONGINT);
  625. VAR i, g, t, max, start, end, prevstart, nextstart: LONGINT;
  626. BEGIN
  627. t := -1; i := 1;
  628. WHILE i < LEN(table) DO
  629. IF IsExtendedPartition(table[i].type) THEN
  630. ASSERT(t = -1); t := i (* at most one extended partition allowed *)
  631. END;
  632. INC(i)
  633. END;
  634. IF t # -1 THEN
  635. start := table[t].start; g := hds * spt; max := start + table[t].size;
  636. LOOP
  637. prevstart := start; end := MAX(LONGINT);
  638. FOR i := 1 TO LEN(table)-1 DO (* find first partition start after or on start *)
  639. IF ~(AosDisks.Primary IN table[i].flags) & (table[i].start >= start) & (table[i].start < end) THEN
  640. end := table[i].start
  641. END
  642. END;
  643. IF end > max THEN end := max END;
  644. (* {start..end-1 is free} *)
  645. IF start MOD g # spt THEN
  646. INC(start, (-start) MOD g + spt) (* start on cylinder boundary, second head *)
  647. END;
  648. DEC(end, end MOD g); (* end on cylinder boundary *)
  649. (* {start..end-1 is free and aligned} *)
  650. IF end-start > 0 THEN NewFree(FreeSpace, table, start, end-start, start-spt, {}) END;
  651. nextstart := MAX(LONGINT);
  652. FOR i := 1 TO LEN(table)-1 DO (* find first partition end after prevstart *)
  653. IF ~(AosDisks.Primary IN table[i].flags) THEN
  654. t := table[i].start+table[i].size-1;
  655. IF (t > prevstart) & (t < nextstart) THEN nextstart := t END
  656. END
  657. END;
  658. IF nextstart = MAX(LONGINT) THEN
  659. EXIT (* no more partitions end after prevstart *)
  660. ELSE
  661. start := nextstart+1
  662. END
  663. END
  664. END
  665. END FindFreeExtended;
  666. PROCEDURE FindReserved(VAR table: AosDisks.PartitionTable);
  667. VAR i, t, max, start, end, prevstart, nextstart: LONGINT;
  668. BEGIN
  669. IF ShowReserved THEN
  670. start := 0; max := table[0].size;
  671. LOOP
  672. prevstart := start; end := MAX(LONGINT);
  673. FOR i := 1 TO LEN(table)-1 DO (* find first partition start after or on start *)
  674. IF (table[i].start >= start) & (table[i].start < end) THEN
  675. end := table[i].start (* free space ends at this start position *)
  676. END
  677. END;
  678. IF end > max THEN end := max END; (* clip to end of disk *)
  679. (* {start..end-1 is free} *)
  680. IF end-start > 0 THEN NewFree(ReservedSpace, table, start, end-start, 0, {AosDisks.Primary}) END;
  681. nextstart := MAX(LONGINT);
  682. FOR i := 1 TO LEN(table)-1 DO (* find first partition end after prevstart *)
  683. t := table[i].start+table[i].size-1;
  684. IF (t > prevstart) & (t < nextstart) THEN nextstart := t END
  685. END;
  686. IF nextstart = MAX(LONGINT) THEN
  687. EXIT (* no more partitions end after prevstart *)
  688. ELSE
  689. start := nextstart+1
  690. END
  691. END
  692. END
  693. END FindReserved;
  694. PROCEDURE CheckTable(w : Streams.Writer; dev: AosDisks.Device; table: AosDisks.PartitionTable): BOOLEAN;
  695. VAR i, j, ext: LONGINT;
  696. BEGIN
  697. ASSERT(w # NIL);
  698. ext := -1;
  699. (* check all partitions for size, and presence of at most one extended partition *)
  700. FOR i := 0 TO LEN(table)-1 DO
  701. IF (table[i].start < 0) OR (table[i].size < 0) OR (table[i].start+table[i].size < 0) THEN
  702. w.String("Warning: "); WritePart(w, dev, i);
  703. w.String("too large"); w.Ln;
  704. RETURN FALSE
  705. END;
  706. IF IsExtendedPartition(table[i].type) THEN
  707. IF ext # -1 THEN
  708. w.String("Error: "); WritePart(w, dev, ext);
  709. w.String("and "); WritePart(w, dev, i);
  710. w.String("are both extended"); w.Ln;
  711. RETURN FALSE
  712. END;
  713. ext := i
  714. END
  715. END;
  716. (* check all primary partitions and logical drives for overlap *)
  717. FOR i := 1 TO LEN(table)-1 DO
  718. IF AosDisks.Primary IN table[i].flags THEN (* primary partition *)
  719. FOR j := 1 TO LEN(table)-1 DO
  720. IF (i # j) & (AosDisks.Primary IN table[j].flags) & PartitionsOverlap(table, i, j) THEN
  721. w.String("Error: "); WritePart(w, dev, i);
  722. w.String("and "); WritePart(w, dev, j);
  723. w.String("overlap"); w.Ln;
  724. RETURN FALSE (* primary partitions can not overlap *)
  725. END
  726. END
  727. ELSE (* logical drive in extended partition *)
  728. FOR j := 1 TO LEN(table)-1 DO
  729. IF (i # j) & (j # ext) & PartitionsOverlap(table, i, j) THEN
  730. w.String("Error: "); WritePart(w, dev, i);
  731. w.String("and "); WritePart(w, dev, j);
  732. w.String("overlap"); w.Ln;
  733. RETURN FALSE (* logical drives can not overlap any other partition, except the extended partition *)
  734. END
  735. END
  736. END
  737. END;
  738. RETURN TRUE
  739. END CheckTable;
  740. (* Find free space on the disk and insert placeholder partitions (table is reallocated). *)
  741. PROCEDURE FindFreeSpace(w : Streams.Writer; dev: AosDisks.Device; VAR table: AosDisks.PartitionTable; spt, hds: LONGINT) : BOOLEAN;
  742. BEGIN
  743. ASSERT(w # NIL);
  744. ASSERT((hds > 0) & (spt > 0) & (table[0].start = 0));
  745. IF CheckTable(w, dev, table) THEN
  746. FindFreePrimary(table, spt, hds);
  747. FindFreeExtended(table, spt, hds);
  748. IF ShowReserved THEN FindReserved(table) END;
  749. RETURN TRUE;
  750. ELSE
  751. RETURN FALSE;
  752. END
  753. END FindFreeSpace;
  754. (* For each partition of the specified disk, try to find the associated file system (if any) *)
  755. PROCEDURE AssignFileSystems(disk : LONGINT);
  756. VAR
  757. fs : Files.FileSystem;
  758. ft : Files.FileSystemTable;
  759. vol : DiskVolumes.Volume;
  760. volOld : OldDiskVolumes.Volume;
  761. volFAT : FATVolumes.Volume;
  762. volISO : ISO9660Volumes.Volume;
  763. dev : AosDisks.Device;
  764. found : BOOLEAN;
  765. partition , fsStart, i : LONGINT;
  766. BEGIN
  767. ASSERT(disk < LEN(disks));
  768. Files.GetList(ft);
  769. FOR i := 0 TO LEN(ft)-1 DO
  770. fs := ft[i];
  771. IF fs.vol # NIL THEN
  772. dev := NIL;
  773. IF fs.vol IS DiskVolumes.Volume THEN
  774. vol := fs.vol (DiskVolumes.Volume); dev := vol.dev; fsStart := vol.startfs;
  775. ELSIF fs.vol IS OldDiskVolumes.Volume THEN
  776. volOld := fs.vol (OldDiskVolumes.Volume); dev := volOld.dev; fsStart := volOld.startfs;
  777. ELSIF fs.vol IS FATVolumes.Volume THEN
  778. volFAT := fs.vol (FATVolumes.Volume); dev := volFAT.dev; fsStart := volFAT.start;
  779. ELSIF fs.vol IS ISO9660Volumes.Volume THEN
  780. volISO := fs.vol (ISO9660Volumes.Volume); dev := volISO.dev;
  781. END;
  782. IF (dev # NIL) & (dev = disks[disk].device) & (disks[disk].device.table # NIL) THEN
  783. IF Trace * TraceGetFS # {} THEN KernelLog.String("Looking for FS of device: "); KernelLog.String(dev.name); KernelLog.Ln; END;
  784. IF IsPartitioned(disks[disk].device) THEN
  785. found := FALSE; partition := 1; (* Partition 0 is WHOLE DISK - ignore for partitioned devices *)
  786. LOOP
  787. IF found OR (partition > LEN(disks[disk].device.table) - 1) THEN EXIT END;
  788. IF ~IsExtendedPartition(disks[disk].device.table[partition].type) THEN (* don't consider extended partitions *)
  789. IF (disks[disk].device.table[partition].start <= fsStart) &
  790. (fsStart < disks[disk].device.table[partition].start + disks[disk].device.table[partition].size) THEN
  791. found := TRUE;
  792. disks[disk].fs[partition] := fs;
  793. IF Trace * TraceGetFS # {} THEN
  794. KernelLog.String(fs.prefix); KernelLog.String(" on "); KernelLog.String(disks[disk].device.name);
  795. KernelLog.String("#"); KernelLog.Int(partition, 0);
  796. END;
  797. END;
  798. END;
  799. INC(partition);
  800. END;
  801. ELSE (* Device is not partitioned *)
  802. disks[disk].fs[0] := fs;
  803. END;
  804. END;
  805. END;
  806. END; (* END FOR *)
  807. END AssignFileSystems;
  808. (* Returns TRUE iff partition i contains sector x. *)
  809. PROCEDURE Contains(table: AosDisks.PartitionTable; i, x: LONGINT): BOOLEAN;
  810. BEGIN
  811. RETURN (table[i].start <= x) & (x < table[i].start + table[i].size)
  812. END Contains;
  813. (* Returns TRUE iff partition i and j overlap *)
  814. PROCEDURE PartitionsOverlap(table: AosDisks.PartitionTable; i, j: LONGINT): BOOLEAN;
  815. BEGIN
  816. RETURN Contains(table, i, table[j].start) OR Contains(table, i, table[j].start+table[j].size-1)
  817. OR Contains(table, j, table[i].start) OR Contains(table, j, table[i].start+table[i].size-1)
  818. END PartitionsOverlap;
  819. PROCEDURE Finalize;
  820. VAR res : WORD;
  821. BEGIN
  822. AosDisks.registry.RemoveEventHandler(DiskEventHandler, res);
  823. END Finalize;
  824. END DisksModel;
  825. TYPE
  826. String* = ARRAY 256 OF CHAR;
  827. OperationState* = RECORD
  828. (** status information *)
  829. status- : SET;
  830. statusString- : String;
  831. (** error information *)
  832. errorCount- : LONGINT;
  833. (** progress *)
  834. progressValid- : BOOLEAN; (* IF progressValid= TRUE the variables min, cur & max contain meaningful values *)
  835. min-, cur-, max- : HUGEINT;
  836. END;
  837. TYPE
  838. (*
  839. * How this works:
  840. *
  841. * 1. Initialize new object instance
  842. * 2. Pass parameters (child object will have parameters as fields)
  843. * 3. IF parameters are valid, set alive := TRUE, else, set alive := FALSE
  844. * 4. Add object to PartitionsLib.registry via Add
  845. * 5. Call SetStart
  846. * 6. As long as StatusRunning IN state.status the object is active
  847. * 7. Terminate object via Abort & AwaitDead
  848. *)
  849. Operation* = OBJECT
  850. VAR
  851. (* Note: The actual implementation may read the state without lock it. All other cases: only access via Set/Get procedures! *)
  852. state- : OperationState;
  853. resultText, infoText, errorsText : Texts.Text;
  854. result-, info-, errors : TextUtilities.TextWriter; (* info, result: write access while operation is running is allowed *)
  855. (* IF TRUE, the WMPartitions.selection field is invalidated (set to none)
  856. Set this flag if the operation changed the length of the partition table; *)
  857. invalidateSelection* : BOOLEAN;
  858. (* set at object instantiation *)
  859. name*, desc* : String;
  860. uid- : LONGINT;
  861. disk- : Disk; partition- : LONGINT;
  862. diskpartString- : String; (* dev#part; Set at &Init *)
  863. starttime-, endtime- : Dates.DateTime; (* endtime only valid if StatusFinished IN state.status *)
  864. (* internal variables *)
  865. alive*, dead, start : BOOLEAN; (* synchronization *)
  866. diskmodel : DisksModel; (* needed for locking *)
  867. next : Operation;
  868. trapped : BOOLEAN;
  869. parent : Operation; (* See procedure SetParent *)
  870. locktype* : LONGINT; (* WriterLock or ReaderLock *)
  871. locked : BOOLEAN;
  872. out- : Streams.Writer;
  873. temp: Strings.String;
  874. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  875. VAR temp : ARRAY 10 OF CHAR;
  876. BEGIN
  877. ASSERT((disk.device # NIL) & (disk.table#NIL) & (partition >= 0) & (partition <LEN(disk.table)));
  878. SELF.disk := disk; SELF.partition := partition;
  879. SELF.out := out;
  880. ASSERT((diskModel#NIL) & (operations#NIL));
  881. locktype := WriterLock; diskmodel := diskModel;
  882. state.errorCount := 0;
  883. alive := TRUE; dead := FALSE; start := FALSE; invalidateSelection := FALSE;
  884. diskpartString := "";
  885. Strings.Append(diskpartString, disk.device.name); Strings.Append(diskpartString, "#");
  886. Strings.IntToStr(partition, temp); Strings.Append(diskpartString, temp);
  887. starttime := Dates.Now();
  888. NEW(resultText); NEW(result, resultText);
  889. NEW(infoText); NEW(info, infoText);
  890. NEW(errorsText); NEW(errors, errorsText);
  891. SetStatus({StatusWaiting}, "Waiting", 0, 0, 0, FALSE);
  892. END Init;
  893. (** synchronized access to the object's state *)
  894. PROCEDURE GetState*() : OperationState;
  895. BEGIN {EXCLUSIVE}
  896. RETURN state;
  897. END GetState;
  898. PROCEDURE SetStatus*(status : SET; CONST statusString : String; min, cur, max : HUGEINT; valid : BOOLEAN);
  899. BEGIN {EXCLUSIVE}
  900. state.status := status; state.statusString := statusString;
  901. state.min := min; state.cur := cur; state.max := max; state.progressValid := valid;
  902. END SetStatus;
  903. PROCEDURE SetCurrentProgress*(cur :HUGEINT);
  904. BEGIN {EXCLUSIVE}
  905. state.cur := cur;
  906. END SetCurrentProgress;
  907. PROCEDURE ReportError*(CONST error : ARRAY OF CHAR);
  908. BEGIN {EXCLUSIVE}
  909. INCL(state.status, StatusError);
  910. INC(state.errorCount);
  911. errors.String("Error "); errors.Int(state.errorCount, 3); errors.String(" : "); errors.String(error); errors.Ln;
  912. END ReportError;
  913. PROCEDURE GetResult*() : Strings.String;
  914. VAR string : Strings.String; length : LONGINT;
  915. BEGIN
  916. string := Strings.NewString(""); result.Update;
  917. resultText.AcquireRead;
  918. length := resultText.GetLength() + 1;
  919. IF (StatusFinished IN state.status) & (length > 0) THEN
  920. NEW(string, length);
  921. TextUtilities.TextToStr(resultText, string^);
  922. END;
  923. resultText.ReleaseRead;
  924. RETURN string;
  925. END GetResult;
  926. PROCEDURE GetInfo*() : Strings.String;
  927. VAR string : Strings.String;
  928. BEGIN
  929. string := Strings.NewString(""); info.Update;
  930. infoText.AcquireRead;
  931. IF (StatusFinished IN state.status) & (infoText.GetLength() > 0) THEN
  932. NEW(string, infoText.GetLength() + 1);
  933. TextUtilities.TextToStr(infoText, string^);
  934. END;
  935. infoText.ReleaseRead;
  936. RETURN string;
  937. END GetInfo;
  938. PROCEDURE GetErrors*() : Strings.String;
  939. VAR string : Strings.String;
  940. BEGIN
  941. string := Strings.NewString(""); errors.Update;
  942. errorsText.AcquireRead;
  943. IF errorsText.GetLength() > 0 THEN
  944. NEW(string, errorsText.GetLength() + 1);
  945. TextUtilities.TextToStr(errorsText, string^);
  946. END;
  947. errorsText.ReleaseRead;
  948. RETURN string;
  949. END GetErrors;
  950. (* Does the actual disk operation. Should RETURN asap when alive is set to FALSE *)
  951. (* The needed parameters will be fields of the operation that inhertis from this object *)
  952. PROCEDURE DoOperation*;
  953. BEGIN
  954. HALT(301); (* abstract *)
  955. END DoOperation;
  956. (* Returns TRUE if the parameters are valid *)
  957. PROCEDURE ValidParameters*() : BOOLEAN;
  958. BEGIN
  959. HALT(301); RETURN FALSE; (* abstract *)
  960. END ValidParameters;
  961. (* the following procedures are FINAL *)
  962. (* The object shall abort its current operation and call SetDead when it's finished *)
  963. PROCEDURE Abort*;
  964. BEGIN {EXCLUSIVE}
  965. INCL(state.status, StatusAborted);
  966. state.statusString := "Aborted";
  967. result.String("Operation aborted");
  968. alive := FALSE; start := TRUE;
  969. END Abort;
  970. PROCEDURE Aborted*() : BOOLEAN;
  971. VAR o : Operation;
  972. BEGIN
  973. o := SELF;
  974. WHILE (o.alive) & (o.parent # NIL) DO
  975. o := o.parent;
  976. END;
  977. RETURN ~o.alive;
  978. END Aborted;
  979. PROCEDURE SetBlockingStart*;
  980. BEGIN
  981. BEGIN {EXCLUSIVE} start := TRUE; END;
  982. (* Release lock to trigger AWAIT(start) *)
  983. BEGIN {EXCLUSIVE} AWAIT(dead); END;
  984. END SetBlockingStart;
  985. (** *)
  986. (* To support the containment relation for operation, a parent operation can declare other operations *)
  987. (* as its child operations. The parent must be set before the child operation is started and must have a lock type *)
  988. (* at least as restrictive as the child has. The child operations then do not try to acquire a partition lock. *)
  989. PROCEDURE SetParent*(operation : Operation);
  990. VAR newName : String;
  991. BEGIN
  992. parent := operation;
  993. COPY(parent.name, newName);
  994. Strings.Append(newName, ".");
  995. Strings.Append(newName, name);
  996. name := newName;
  997. ASSERT(parent.locktype <= locktype);
  998. END SetParent;
  999. PROCEDURE Indent;
  1000. VAR operation : Operation;
  1001. BEGIN
  1002. ASSERT(out # NIL);
  1003. operation := parent;
  1004. WHILE (operation # NIL) DO
  1005. out.Char(9X);
  1006. operation := operation.parent;
  1007. END;
  1008. END Indent;
  1009. (* synchronization procedures *)
  1010. PROCEDURE SetDead; BEGIN {EXCLUSIVE} dead := TRUE; END SetDead;
  1011. PROCEDURE AwaitDead*; BEGIN {EXCLUSIVE} AWAIT(dead); END AwaitDead;
  1012. PROCEDURE SetStart*; BEGIN {EXCLUSIVE} start := TRUE; END SetStart;
  1013. PROCEDURE AwaitStart; BEGIN {EXCLUSIVE} AWAIT(start); END AwaitStart;
  1014. PROCEDURE GetReport*(details : BOOLEAN) : Texts.Text;
  1015. VAR
  1016. text : Texts.Text; w : TextUtilities.TextWriter;
  1017. temp : ARRAY 64 OF CHAR;
  1018. BEGIN {EXCLUSIVE}
  1019. NEW(text); NEW(w, text);
  1020. w.SetFontStyle({WMGraphics.FontBold}); w.String("Operation: "); w.SetFontStyle({});
  1021. w.String("UID "); w.Int(uid, 0); w.String(": "); w.String(name); w.Ln;
  1022. IF ~details THEN
  1023. w.String(" on "); w.String(diskpartString); w.String(", Status: ");
  1024. IF ~(StatusFinished IN state.status) THEN
  1025. w.String(state.statusString);
  1026. IF state.progressValid THEN
  1027. w.String(" (Progress: "); w.Int(ENTIER(100.0 * state.cur / state.max), 1); w.String("%"); w.String(")");
  1028. ELSE
  1029. w.String(" (Running)");
  1030. END;
  1031. ELSE
  1032. w.Update; result.Update;
  1033. resultText.AcquireRead; text.AcquireWrite;
  1034. text.CopyFromText(resultText, 0, resultText.GetLength(), text.GetLength());
  1035. text.ReleaseWrite; resultText.ReleaseRead;
  1036. END;
  1037. IF state.errorCount > 0 THEN w.String(", errors: "); w.Int(state.errorCount, 0); END;
  1038. w.Ln; w.Update;
  1039. ELSE
  1040. w.SetFontStyle({WMGraphics.FontBold}); w.String("Description: "); w.SetFontStyle({}); w.String(desc); w.Ln;
  1041. (* Target information *)
  1042. w.SetFontStyle({WMGraphics.FontBold}); w.String("Target: "); w.SetFontStyle({});
  1043. IF disk.device # NIL THEN
  1044. w.String(diskpartString); w.String(" ("); w.String(disk.device.desc); w.String(")");
  1045. ELSE
  1046. w.String("Unknown");
  1047. END;
  1048. w.Ln;
  1049. (* Time information *)
  1050. w.SetFontStyle({WMGraphics.FontBold}); w.String("Started: "); w.SetFontStyle({});
  1051. Strings.FormatDateTime(DateTimeFormat, starttime, temp); w.String(temp);
  1052. IF StatusFinished IN state.status THEN
  1053. w.SetFontStyle({WMGraphics.FontBold}); w.String(" Ended: "); w.SetFontStyle({});
  1054. Strings.FormatDateTime(DateTimeFormat, endtime, temp); w.String(temp);
  1055. END;
  1056. w.Ln;
  1057. (* Status information *)
  1058. w.SetFontStyle({WMGraphics.FontBold}); w.String("Status: "); w.SetFontStyle({});
  1059. IF StatusFinished IN state.status THEN
  1060. w.Update; result.Update;
  1061. resultText.AcquireRead;
  1062. text.AcquireWrite;
  1063. text.CopyFromText(resultText, 0, resultText.GetLength(), text.GetLength());
  1064. text.ReleaseWrite;
  1065. resultText.ReleaseRead;
  1066. w.Ln;
  1067. ELSE
  1068. w.String(state.statusString);
  1069. IF ~(StatusFinished IN state.status) & state.progressValid THEN
  1070. w.String(" (Progress: "); w.Int(ENTIER(100.0 * state.cur / state.max), 1); w.String("%"); w.String(")");
  1071. END;
  1072. END;
  1073. w.Ln;
  1074. (* Append error text *)
  1075. errors.Update;
  1076. w.SetFontStyle({WMGraphics.FontBold}); w.String("Errors: "); w.SetFontStyle({});
  1077. IF state.errorCount > 0 THEN
  1078. w.Int(state.errorCount, 0); w.Ln; w.Update;
  1079. errorsText.AcquireRead; text.AcquireWrite;
  1080. text.CopyFromText(errorsText, 0, errorsText.GetLength(), text.GetLength());
  1081. text.ReleaseWrite; errorsText.ReleaseRead;
  1082. ELSE
  1083. w.String("none"); w.Update;
  1084. END;
  1085. w.Ln; w.Ln;
  1086. (* Append information text *)
  1087. info.Update;
  1088. infoText.AcquireRead;
  1089. IF infoText.GetLength() > 0 THEN
  1090. w.SetFontStyle({WMGraphics.FontBold}); w.String("Information:"); w.SetFontStyle({}); w.Ln; w.Update;
  1091. text.AcquireWrite;
  1092. text.CopyFromText(infoText, 0, infoText.GetLength(), text.GetLength());
  1093. text.ReleaseWrite;
  1094. END;
  1095. infoText.ReleaseRead;
  1096. END;
  1097. RETURN text;
  1098. END GetReport;
  1099. PROCEDURE Show*(out : Streams.Writer; details : BOOLEAN);
  1100. VAR text : Texts.Text; string : Strings.String;
  1101. BEGIN
  1102. text := GetReport(details);
  1103. text.AcquireRead;
  1104. NEW(string, text.GetLength() + 1);
  1105. TextUtilities.TextToStr(text, string^);
  1106. text.ReleaseRead;
  1107. out.String("Partitions: "); out.String(string^);
  1108. END Show;
  1109. (* Returns TRUE if operation trapped *)
  1110. PROCEDURE SafelyDoOperation() : BOOLEAN;
  1111. VAR trap, opened : BOOLEAN; res : WORD; temp: ARRAY 256 OF CHAR;
  1112. BEGIN
  1113. trap := FALSE; opened := FALSE;
  1114. IF (disk.device # NIL) THEN
  1115. disk.device.Open(res);
  1116. IF res = AosDisks.Ok THEN
  1117. opened := TRUE;
  1118. DoOperation;
  1119. ELSE
  1120. GetErrorMsg("Could not open device: ", res, temp); ReportError(temp);
  1121. END;
  1122. ELSE
  1123. ReportError("Could not open device: No device exists");
  1124. END;
  1125. FINALLY
  1126. IF opened & (disk.device # NIL) THEN
  1127. disk.device.Close(res);
  1128. IF res # AosDisks.Ok THEN
  1129. GetErrorMsg("Could not close device: ", res, temp); ReportError(temp);
  1130. END;
  1131. END;
  1132. RETURN trap;
  1133. END SafelyDoOperation;
  1134. BEGIN {ACTIVE}
  1135. locked := FALSE;
  1136. AwaitStart;
  1137. SetStatus({StatusRunning}, "Running", 0, 0, 0, FALSE);
  1138. IF (out # NIL) THEN Indent; out.String(desc); out.String(" "); out.String(diskpartString); out.String(" ... "); out.Update; END;
  1139. IF alive THEN
  1140. operations.Add(SELF);
  1141. IF ValidParameters() THEN
  1142. IF (parent # NIL) & (parent.locktype <= locktype) THEN
  1143. trapped := SafelyDoOperation();
  1144. ELSIF (parent = NIL) & (diskmodel.AcquirePartition(disk, partition, locktype)) THEN
  1145. locked := TRUE;
  1146. trapped := SafelyDoOperation();
  1147. ELSE
  1148. ReportError("Partition is locked"); result.String("Partition is locked");
  1149. IF (out # NIL) THEN out.String("partition is locked."); out.Ln; out.Update; END;
  1150. END;
  1151. IF locked THEN diskmodel.ReleasePartition(disk, partition); END;
  1152. IF (locktype = WriterLock) THEN
  1153. diskModel.UpdateDisk(disk);
  1154. END;
  1155. ELSE
  1156. result.String("Wrong Parameters");
  1157. IF (out # NIL) THEN
  1158. out.String("invalid parameters."); out.Ln;
  1159. temp := GetErrors (); Indent; out.String(temp^); out.Ln; out.Update;
  1160. END;
  1161. END;
  1162. ELSE
  1163. (* operation has been aborted before it started *)
  1164. END;
  1165. endtime := Dates.Now();
  1166. IF trapped THEN
  1167. ReportError("Operation trapped");
  1168. SetStatus(state.status, "TRAPPED", state.min, state.cur, state.max, state.progressValid);
  1169. IF (out # NIL) THEN out.String("trapped."); out.Ln; out.Update; END;
  1170. ELSIF ~(StatusAborted IN state.status) THEN
  1171. SetStatus(state.status + {StatusFinished}, "Finished", state.min, state.cur, state.max, state.progressValid);
  1172. IF (out # NIL) THEN
  1173. IF (state.errorCount = 0) THEN
  1174. info.Update;
  1175. infoText.AcquireRead;
  1176. IF (infoText.GetLength() > 0) THEN
  1177. temp := GetInfo (); out.Ln; Indent; out.String(" "); out.String(temp^); Indent;
  1178. END;
  1179. infoText.ReleaseRead;
  1180. out.String("done.");
  1181. ELSE
  1182. temp := GetErrors(); out.Ln; Indent; out.String(temp^);
  1183. END;
  1184. out.Ln; out.Update;
  1185. END;
  1186. ELSE
  1187. SetStatus(state.status, "Aborted", state.min, state.cur, state.max, state.progressValid);
  1188. IF (out # NIL) THEN out.String("aborted."); out.Ln; out.Update; END;
  1189. END;
  1190. infobus.ReportCompletion(SELF);
  1191. SetDead;
  1192. END Operation;
  1193. TYPE
  1194. AllOperations* = POINTER TO ARRAY OF Operation;
  1195. TYPE
  1196. OperationManager* = OBJECT
  1197. VAR
  1198. onChanged- : WMEvents.EventSource; (* notify when operations added/removed *)
  1199. operations : Operation;
  1200. uid : LONGINT;
  1201. (** Add the specified operation *)
  1202. PROCEDURE Add(operation : Operation);
  1203. BEGIN {EXCLUSIVE}
  1204. ASSERT(operation # NIL);
  1205. operation.uid := GetUid();
  1206. IF operations = NIL THEN
  1207. operations := operation;
  1208. ELSE
  1209. operation.next := operations;
  1210. operations := operation;
  1211. END;
  1212. onChanged.Call(NIL);
  1213. END Add;
  1214. (** Remove the specified operation. Returns FALSE if operation not found *)
  1215. PROCEDURE Remove*(operation : Operation) : BOOLEAN;
  1216. VAR temp : Operation; found : BOOLEAN;
  1217. BEGIN {EXCLUSIVE}
  1218. ASSERT(operation#NIL);
  1219. found := FALSE;
  1220. IF operations = operation THEN
  1221. found := TRUE;
  1222. IF ~(StatusFinished IN operation.state.status) THEN (* operation is still running *)
  1223. Terminate(operation);
  1224. END;
  1225. operations := operations.next;
  1226. ELSE
  1227. temp := operations;
  1228. IF temp#NIL THEN
  1229. WHILE (temp.next # operation) & (temp.next # NIL) DO temp := temp.next; END;
  1230. IF temp.next = operation THEN (* found *)
  1231. found := TRUE;
  1232. IF ~(StatusFinished IN operation.state.status) THEN (* operation is still running *)
  1233. Terminate(operation);
  1234. END;
  1235. temp.next := temp.next.next;
  1236. END;
  1237. END;
  1238. END;
  1239. IF found THEN onChanged.Call(GetAllInternal()); END;
  1240. RETURN found;
  1241. END Remove;
  1242. (** Get a operation object by its UID. Returns NIL if not found *)
  1243. PROCEDURE GetByUid*(uid : LONGINT) : Operation;
  1244. VAR temp : Operation;
  1245. BEGIN {EXCLUSIVE}
  1246. temp := operations;
  1247. WHILE (temp # NIL) & (temp.uid # uid) DO temp := temp.next; END;
  1248. RETURN temp;
  1249. END GetByUid;
  1250. PROCEDURE GetAll*() : AllOperations;
  1251. BEGIN {EXCLUSIVE}
  1252. RETURN GetAllInternal();
  1253. END GetAll;
  1254. PROCEDURE GetAllInternal*() : AllOperations;
  1255. VAR temp : Operation; result : AllOperations; i, num : LONGINT;
  1256. BEGIN (* caller holds lock on operation manager *)
  1257. temp := operations;
  1258. IF temp # NIL THEN
  1259. num := 0;
  1260. WHILE (temp#NIL) DO temp := temp.next; INC(num); END;
  1261. NEW(result, num);
  1262. temp := operations; i := 0;
  1263. WHILE (temp#NIL) DO result[i] := temp; temp := temp.next; INC(i); END;
  1264. END;
  1265. RETURN result;
  1266. END GetAllInternal;
  1267. (** Remove the specified operation. Returns FALSE if the operation has not been found *)
  1268. PROCEDURE RemoveByUid*(uid : LONGINT) : BOOLEAN;
  1269. VAR temp : Operation; found : BOOLEAN;
  1270. BEGIN {EXCLUSIVE}
  1271. temp := operations; found := FALSE;
  1272. IF temp # NIL THEN
  1273. IF temp.uid = uid THEN
  1274. found := TRUE;
  1275. IF ~(StatusFinished IN temp.state.status) THEN (* operation is still running *)
  1276. Terminate(temp);
  1277. END;
  1278. operations := operations.next;
  1279. ELSE
  1280. WHILE (temp.next # NIL) & (temp.next.uid # uid) DO temp := temp.next; END;
  1281. IF temp.next # NIL THEN
  1282. found := TRUE;
  1283. IF ~(StatusFinished IN temp.state.status) THEN (* operation is still running *)
  1284. Terminate(temp.next);
  1285. END;
  1286. temp.next := temp.next.next;
  1287. END;
  1288. END;
  1289. END;
  1290. IF found THEN onChanged.Call(GetAllInternal()); END;
  1291. RETURN found;
  1292. END RemoveByUid;
  1293. (** Remove all (finished) operations. Returns the number of removed operations *)
  1294. PROCEDURE RemoveAll*(finishedOnly : BOOLEAN) : LONGINT;
  1295. VAR temp : Operation; counter : LONGINT;
  1296. BEGIN {EXCLUSIVE}
  1297. temp := operations; counter := 0;
  1298. IF finishedOnly THEN
  1299. IF temp # NIL THEN
  1300. WHILE (temp # NIL) & (temp.next # NIL) DO
  1301. IF StatusFinished IN temp.next.state.status THEN
  1302. temp.next := temp.next.next;
  1303. INC(counter);
  1304. ELSE
  1305. temp := temp.next;
  1306. END;
  1307. END;
  1308. (* now look at the head of the list *)
  1309. IF StatusFinished IN operations.state.status THEN
  1310. operations := operations.next;
  1311. INC(counter);
  1312. END;
  1313. END;
  1314. ELSE
  1315. WHILE (temp # NIL) DO (* terminate operations which are still in progress *)
  1316. IF ~(StatusFinished IN temp.state.status) THEN
  1317. Terminate(temp);
  1318. END;
  1319. INC(counter);
  1320. temp := temp.next;
  1321. END;
  1322. operations := NIL;
  1323. END;
  1324. IF counter > 0 THEN onChanged.Call(GetAllInternal()); END;
  1325. RETURN counter;
  1326. END RemoveAll;
  1327. PROCEDURE Terminate(operation : Operation);
  1328. BEGIN (* caller must hold lock on operation manager*)
  1329. ASSERT(operation # NIL);
  1330. KernelLog.String("Terminating plugin "); KernelLog.String(operation.desc); KernelLog.String(" on ");
  1331. KernelLog.String(operation.name); KernelLog.String("...");
  1332. operation.Abort;
  1333. operation.AwaitDead;
  1334. KernelLog.String("done."); KernelLog.Ln;
  1335. END Terminate;
  1336. PROCEDURE Show*(out : Streams.Writer; details : BOOLEAN);
  1337. VAR operation : Operation;
  1338. BEGIN {EXCLUSIVE}
  1339. ASSERT(out # NIL);
  1340. out.String("Partitions: Currently pending disk operations: "); out.Ln;
  1341. IF operations = NIL THEN
  1342. out.String("None"); out.Ln;
  1343. ELSE
  1344. operation:= operations;
  1345. WHILE (operation # NIL) DO
  1346. operation.Show(out, details);
  1347. operation := operation.next;
  1348. END;
  1349. END;
  1350. END Show;
  1351. PROCEDURE GetUid() : LONGINT;
  1352. BEGIN (* caller must hold lock on operation manager *)
  1353. INC(uid); ASSERT(uid >= 0); (* LONGINT overflow *)
  1354. RETURN uid;
  1355. END GetUid;
  1356. PROCEDURE Finalize;
  1357. VAR ignore : LONGINT;
  1358. BEGIN
  1359. ignore := RemoveAll(FALSE);
  1360. END Finalize;
  1361. PROCEDURE &Init*;
  1362. BEGIN
  1363. uid := -1;
  1364. NEW(onChanged, SELF,Strings.NewString("OperationsChanged"), NIL, NIL);
  1365. END Init;
  1366. END OperationManager;
  1367. TYPE
  1368. ListenerProcedure = PROCEDURE {DELEGATE} (operation : Operation; CONST message : ARRAY OF CHAR);
  1369. Listener = POINTER TO RECORD
  1370. proc : ListenerProcedure;
  1371. next : Listener;
  1372. END;
  1373. CompletionNotification* = OBJECT
  1374. VAR
  1375. listeners : Listener;
  1376. PROCEDURE &Init;
  1377. BEGIN
  1378. listeners := NIL;
  1379. END Init;
  1380. PROCEDURE AddListener*(proc : ListenerProcedure);
  1381. VAR l : Listener;
  1382. BEGIN {EXCLUSIVE}
  1383. ASSERT(proc # NIL);
  1384. NEW(l); l.proc := proc;
  1385. l.next := listeners;
  1386. listeners := l;
  1387. END AddListener;
  1388. PROCEDURE RemoveListener*(proc : ListenerProcedure);
  1389. VAR temp : Listener;
  1390. BEGIN {EXCLUSIVE}
  1391. ASSERT((listeners # NIL) & (proc # NIL));
  1392. IF (listeners.proc = proc) THEN
  1393. listeners := listeners.next;
  1394. ELSE
  1395. temp := listeners;
  1396. WHILE (temp.next # NIL) & (temp.next.proc # proc) DO temp := temp.next; END;
  1397. ASSERT(temp # NIL);
  1398. temp.next := temp.next.next;
  1399. END;
  1400. END RemoveListener;
  1401. PROCEDURE NotifyListeners(operation : Operation; CONST message : ARRAY OF CHAR);
  1402. VAR l : Listener;
  1403. BEGIN {EXCLUSIVE}
  1404. ASSERT(operation # NIL);
  1405. l := listeners;
  1406. WHILE (l # NIL) DO
  1407. l.proc(operation, message);
  1408. l := l.next;
  1409. END;
  1410. END NotifyListeners;
  1411. PROCEDURE ReportCompletion*(operation : Operation);
  1412. VAR string : Strings.String; message : ARRAY 256 OF CHAR;
  1413. BEGIN
  1414. ASSERT(operation # NIL);
  1415. string := operation.GetResult();
  1416. IF (string # NIL) THEN
  1417. COPY(string^, message);
  1418. ELSE
  1419. message := "";
  1420. END;
  1421. NotifyListeners(operation, message);
  1422. END ReportCompletion;
  1423. END CompletionNotification;
  1424. CONST
  1425. BlocksPerTransfer = 128;
  1426. TYPE
  1427. (* Base class of PartitionToFile & FileToPartition operations *)
  1428. Image* = OBJECT(Operation);
  1429. VAR
  1430. block, numblocks, blocksize : LONGINT;
  1431. filename : Files.FileName;
  1432. buffer : POINTER TO ARRAY OF CHAR;
  1433. (* Parameters: dev#part name [block numblocks] *)
  1434. PROCEDURE SetParameters*(CONST name : ARRAY OF CHAR; block, numblocks: LONGINT);
  1435. BEGIN
  1436. filename := ""; Strings.Append(filename, name); SELF.block := block; SELF.numblocks := numblocks;
  1437. END SetParameters;
  1438. PROCEDURE ValidParameters*() : BOOLEAN;
  1439. VAR res : WORD; temp: ARRAY 256 OF CHAR;
  1440. BEGIN
  1441. IF (block = -1) & (numblocks = -1) THEN (* optional parameters block & numblocks not set; whole partition *)
  1442. block := 0;
  1443. disk.device.GetSize(numblocks, res);
  1444. IF res # AosDisks.Ok THEN
  1445. GetErrorMsg("GetSize failed: ", res, temp); ReportError(temp); RETURN FALSE;
  1446. END;
  1447. END;
  1448. IF block < 0 THEN ReportError("<Block> parameter must be >= 0"); RETURN FALSE; END;
  1449. IF numblocks < 1 THEN ReportError("<Numblocks> parameter must be > 0"); RETURN FALSE; END;
  1450. blocksize := disk.device.blockSize;
  1451. IF blocksize > 0 THEN
  1452. info.String("Blocksize: "); info.Int(blocksize, 0); info.String(" Bytes"); info.Ln;
  1453. ELSE
  1454. ReportError("Could not get blocksize of device"); RETURN FALSE;
  1455. END;
  1456. RETURN TRUE;
  1457. END ValidParameters;
  1458. END Image;
  1459. TYPE
  1460. PartitionToFile* = OBJECT(Image);
  1461. PROCEDURE DoOperation*;
  1462. VAR f : Files.File; w: Files.Writer; error : String; pos, i, num : LONGINT; res : WORD; temp: ARRAY 256 OF CHAR;
  1463. BEGIN
  1464. f := Files.New(filename);
  1465. IF f # NIL THEN
  1466. IF block + numblocks > disk.table[partition].size THEN
  1467. numblocks := disk.table[partition].size - block;
  1468. info.String("Warning: Partition too small. Using lower numblocks: "); info.Int(numblocks, 0); info.Ln;
  1469. END;
  1470. SetStatus(state.status, "Copying...", 0, 0, numblocks, TRUE);
  1471. NEW(w, f, 0); NEW(buffer, BlocksPerTransfer * blocksize);
  1472. pos := disk.table[partition].start + block;
  1473. num := BlocksPerTransfer; i := 0;
  1474. LOOP
  1475. IF num >= numblocks - i THEN num := numblocks - i END;
  1476. IF (num = 0) OR ~alive THEN EXIT END;
  1477. disk.device.Transfer(AosDisks.Read, pos, num, buffer^, 0, res);
  1478. IF res # AosDisks.Ok THEN
  1479. GetTransferError(disk.device, AosDisks.Read, pos, res, temp); ReportError(temp); EXIT;
  1480. END;
  1481. w.Bytes(buffer^, 0, num*blocksize); ASSERT(w.res = 0); w.Update;
  1482. INC(pos, num); INC(i, num);
  1483. SetCurrentProgress(state.cur + num);
  1484. END;
  1485. IF alive THEN
  1486. Files.Register(f);
  1487. result.Int(i, 0); result.String(" blocks written to "); result.String(filename);
  1488. END;
  1489. ELSE
  1490. error := "Could not create file: "; Strings.Append(error, filename); ReportError(error);
  1491. END;
  1492. END DoOperation;
  1493. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  1494. BEGIN
  1495. Init^(disk, partition, out);
  1496. name := "PartitionToFile"; desc := "Write a partition to a file"; locktype := ReaderLock;
  1497. END Init;
  1498. END PartitionToFile;
  1499. TYPE
  1500. FileToPartition* = OBJECT(Image);
  1501. PROCEDURE DoOperation*; (** dev#part filename [block numblocks] ~ *)
  1502. VAR f: Files.File; r: Files.Reader; error : String; len, pos, num, i : LONGINT; res: WORD;
  1503. BEGIN
  1504. f := Files.Old(filename);
  1505. IF f # NIL THEN
  1506. num := (f.Length() + blocksize - 1) DIV blocksize;
  1507. IF numblocks > num THEN
  1508. numblocks := num;
  1509. info.String("Warning: Specified number of blocks is bigger than image file size. Set numblocks to "); info.Int(numblocks, 0); info.Ln;
  1510. END;
  1511. IF block + numblocks > disk.table[partition].size THEN
  1512. numblocks := disk.table[partition].size - block;
  1513. info.String("Warning: Partition too small. Writing only "); info.Int(numblocks, 0); info.String(" to partition"); info.Ln;
  1514. END;
  1515. SetStatus(state.status, "Copying", 0, 0, numblocks, TRUE);
  1516. NEW(r, f, 0); NEW(buffer, BlocksPerTransfer * blocksize);
  1517. pos := disk.table[partition].start + block;
  1518. i := 0; num := BlocksPerTransfer;
  1519. LOOP
  1520. IF num >= numblocks - i THEN num := numblocks - i; END;
  1521. IF (num = 0) OR ~alive THEN EXIT; END;
  1522. r.Bytes(buffer^, 0, num*blocksize, len);
  1523. WHILE len MOD blocksize # 0 DO buffer^[len] := 0X; INC(len) END;
  1524. ASSERT((disk.table[partition].start <= pos) & (pos + num <= disk.table[partition].start + disk.table[partition].size ));
  1525. disk.device.Transfer(AosDisks.Write, pos, num, buffer^, 0, res);
  1526. IF res # AosDisks.Ok THEN
  1527. GetTransferError(disk.device, AosDisks.Write, pos, res, error); ReportError(error); EXIT
  1528. END;
  1529. INC(pos, num); INC(i, num);
  1530. SetCurrentProgress(state.cur + num);
  1531. END;
  1532. IF alive THEN result.Int(numblocks, 0); result.String(" blocks written to "); result.String(diskpartString); END;
  1533. ELSE
  1534. error := ""; Strings.Append(error, filename); Strings.Append(error, " not found"); ReportError(error);
  1535. END;
  1536. END DoOperation;
  1537. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  1538. BEGIN
  1539. Init^(disk, partition, out);
  1540. name := "FileToPartition"; desc := "Write file to partition"; locktype := WriterLock;
  1541. END Init;
  1542. END FileToPartition;
  1543. TYPE
  1544. Mount* = OBJECT(Operation)
  1545. VAR
  1546. prefix : Files.Prefix;
  1547. alias : ARRAY 64 OF CHAR;
  1548. volumePars, fsPars : ARRAY 64 OF CHAR;
  1549. PROCEDURE SetParameters*(CONST prefix, alias, volumePars, fsPars : ARRAY OF CHAR);
  1550. BEGIN
  1551. COPY(prefix, SELF.prefix);
  1552. COPY(alias, SELF.alias);
  1553. COPY(volumePars, SELF.volumePars);
  1554. COPY(fsPars, SELF.fsPars);
  1555. END SetParameters;
  1556. PROCEDURE ValidParameters*() : BOOLEAN;
  1557. BEGIN
  1558. IF (prefix = "") THEN ReportError("No prefix specified"); RETURN FALSE;
  1559. ELSIF (alias = "") THEN ReportError("No alias specified"); RETURN FALSE;
  1560. END;
  1561. RETURN TRUE;
  1562. END ValidParameters;
  1563. PROCEDURE DoOperation*; (** prefix alias [volpar] ["|" fspar] ~ *)
  1564. VAR
  1565. errorString, par : ARRAY 512 OF CHAR;
  1566. context : Commands.Context;
  1567. arg : Streams.StringReader; errors : Streams.StringWriter;
  1568. msg: ARRAY 64 OF CHAR;
  1569. res : WORD;
  1570. BEGIN
  1571. par := "";
  1572. Strings.Append(par, prefix); Strings.Append(par, " ");
  1573. Strings.Append(par, alias); Strings.Append(par, " ");
  1574. Strings.Append(par, diskpartString); Strings.Append(par, " ");
  1575. Strings.Append(par, volumePars); Strings.Append(par, " ");
  1576. Strings.Append(par, fsPars);
  1577. errorString := "";
  1578. NEW(errors, 512);
  1579. NEW(arg, 512); arg.SetRaw(par, 0, 512);
  1580. NEW(context, NIL, arg, NIL, errors, SELF);
  1581. Commands.Activate("FSTools.Mount", context, {Commands.Wait}, res, msg);
  1582. errors.Get(errorString);
  1583. IF (errorString # "") THEN
  1584. ReportError(errorString);
  1585. ELSE
  1586. result.String(prefix); result.String(" mounted");
  1587. END;
  1588. END DoOperation;
  1589. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  1590. BEGIN
  1591. Init^(disk, partition, out);
  1592. name := "Mount"; desc := "Mount partition"; locktype := WriterLock;
  1593. END Init;
  1594. END Mount;
  1595. TYPE
  1596. CheckPartition* = OBJECT(Operation);
  1597. VAR
  1598. timer : Kernel.MilliTimer;
  1599. PROCEDURE ValidParameters*() : BOOLEAN;
  1600. BEGIN
  1601. RETURN TRUE; (* operation has no parameters *)
  1602. END ValidParameters;
  1603. (* parameters : dev#part *)
  1604. PROCEDURE DoOperation*;
  1605. CONST Size = 16*512; Max = 50;
  1606. VAR
  1607. maxblocks : LONGINT;
  1608. buf : ARRAY Size OF CHAR;
  1609. start, size : LONGINT;
  1610. seed, len, i, ticks: LONGINT; res: WORD;
  1611. temp: ARRAY 256 OF CHAR;
  1612. BEGIN
  1613. start := disk.table[partition].start; size := disk.table[partition].size;
  1614. SetStatus(state.status, "Random check", 0, 0, Max+size, TRUE);
  1615. maxblocks := Size DIV disk.device.blockSize; ASSERT(maxblocks > 0);
  1616. seed := 8872365; res := AosDisks.Ok;
  1617. LOOP
  1618. i := Random(seed, size);
  1619. disk.device.Transfer(AosDisks.Read, start + i, 1, buf, 0, res);
  1620. IF res # AosDisks.Ok THEN GetTransferError(disk.device, AosDisks.Read, start + i, res, temp); ReportError (temp) END;
  1621. SetCurrentProgress(state.cur + 1);
  1622. IF (state.cur >= Max) OR (res # AosDisks.Ok) OR ~alive THEN EXIT END;
  1623. END;
  1624. IF alive & (res = AosDisks.Ok) THEN
  1625. SetStatus(state.status, "Seq Read check", state.min, state.cur, state.max, TRUE);
  1626. Kernel.SetTimer(timer, MAX(LONGINT)); (* performance monitoring *)
  1627. i := 0;
  1628. LOOP
  1629. len := maxblocks;
  1630. IF len > size-i THEN len := size-i END;
  1631. disk.device.Transfer(AosDisks.Read, start + i, len, buf, 0, res);
  1632. IF res # AosDisks.Ok THEN GetTransferError(disk.device, AosDisks.Read, start + i, res, temp); ReportError(temp); END;
  1633. SetCurrentProgress(state.cur+len);
  1634. INC(i, len);
  1635. IF (i >= size) OR (res # AosDisks.Ok) OR ~alive THEN EXIT END;
  1636. END;
  1637. END;
  1638. IF res = AosDisks.Ok THEN
  1639. ticks := Kernel.Elapsed(timer);
  1640. IF (ticks # 0) & (i # 0) THEN
  1641. WriteK(info, i DIV 2); info.String(" KB read in "); info.Int(ticks DIV 1000, 1); info.String("s (");
  1642. info.Int(((i DIV 2) DIV ticks) * 1000, 1); info.String(" KB/s)");
  1643. END;
  1644. result.String("Partition "); result.String(diskpartString); result.String(" has no errors");
  1645. ELSE
  1646. result.String("Encountered "); result.Int(state.errorCount, 1); result.String("errors");
  1647. END;
  1648. END DoOperation;
  1649. (* Pseudo-random number. *)
  1650. PROCEDURE Random (VAR seed: LONGINT; N :LONGINT): LONGINT;
  1651. BEGIN
  1652. (* this is not a good one, but ok for this purpose *)
  1653. seed := (seed + 773) * 13 MOD 9999991;
  1654. RETURN seed MOD N
  1655. END Random;
  1656. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  1657. BEGIN
  1658. Init^(disk, partition, out);
  1659. name := "CheckPartition"; desc := "Sequentially read all blocks on partition"; locktype := ReaderLock;
  1660. END Init;
  1661. END CheckPartition;
  1662. TYPE
  1663. (* Format a partition with an N2KFS or AosFS. *)
  1664. FormatPartition* = OBJECT(Operation);
  1665. VAR
  1666. (* parameters: *)
  1667. fsRes : LONGINT;
  1668. fsName, bootName : ARRAY 256 OF CHAR;
  1669. flag : LONGINT;
  1670. dev : AosDisks.Device; (* set to disk.device in &Init *)
  1671. (* fsname : ["AosFS" | "NatFS" | "NatFS2"] *)
  1672. (* bootname : Name of the bootloader; if bootname="" -> Use Bootname constant *)
  1673. (* fsRes : file system reserved space; fsRes = -1 -> Bootfile size; fsRes = -2 -> Use FSRes constant *)
  1674. (* fl : flag *)
  1675. PROCEDURE SetParameters*(CONST fsname, bootname : ARRAY OF CHAR; fsRes : LONGINT; fl : LONGINT );
  1676. BEGIN
  1677. fsName := ""; Strings.Append(fsName, fsname); SELF.fsRes := fsRes;
  1678. IF fl = 0 THEN flag := 1FH; (* default *) ELSE flag := fl; END;
  1679. COPY(bootname, bootName);
  1680. END SetParameters;
  1681. PROCEDURE ValidParameters*() : BOOLEAN;
  1682. BEGIN
  1683. IF disk.device.blockSize # BS THEN ReportError("Blocksize not supported"); RETURN FALSE; END;
  1684. IF (partition # 0) & ~(AosDisks.Valid IN disk.table[partition].flags) THEN ReportError("Partition not valid"); RETURN FALSE; END;
  1685. IF ~(((partition = 0) & (LEN(disk.table) = 1)) OR IsNativeType(disk.table[partition].type)) THEN
  1686. ReportError("Cannot format selected partition"); RETURN FALSE;
  1687. END;
  1688. IF AosDisks.Mounted IN disk.table[partition].flags THEN ReportError("Cannot format mounted partition"); RETURN FALSE; END;
  1689. IF fsRes < -2 THEN ReportError("Wrong fsRes value"); RETURN FALSE; END;
  1690. IF (fsName # "NatFS") & (fsName # "NatFS2") & (fsName # "AosFS") THEN
  1691. ReportError("Specified file system unknown (use AosFS, NatFS or NatFS2)"); RETURN FALSE;
  1692. END;
  1693. RETURN TRUE;
  1694. END ValidParameters;
  1695. (** dev#part [ "AosFS" | "NatFS" | "NatFS2" [ FSRes [ BootFile [ Flag ] ] ] ] ~ *)
  1696. PROCEDURE DoOperation*;
  1697. VAR
  1698. f : Files.File;
  1699. error : String;
  1700. fs, type, size : LONGINT; res : WORD;
  1701. BEGIN
  1702. IF (fsName = "NatFS") OR (fsName = "NatFS1") THEN type := NativeType1
  1703. ELSIF fsName = "NatFS2" THEN type := NativeType2
  1704. ELSE type := AosType
  1705. END;
  1706. safe := FALSE;
  1707. fs := DetectFS(disk.device, partition);
  1708. IF ~safe OR (fs = UnknownFS) THEN
  1709. disk.device.GetSize(size, res);
  1710. IF res = AosDisks.Ok THEN
  1711. f := Files.Old(bootName);
  1712. IF (f # NIL) OR (fsRes # -1) THEN
  1713. IF fsRes = -1 THEN fsRes := (f.Length()+BS-1) DIV BS;
  1714. ELSIF fsRes = -2 THEN fsRes := FSRes;
  1715. ELSE fsRes := fsRes * 1024 DIV BS;
  1716. END;
  1717. info.String("Reserving "); WriteK(info, fsRes*BS DIV 1024); info.String(" for boot file"); info.Ln;
  1718. CASE type OF
  1719. AosType: InitAosFS(fsRes, flag, res)
  1720. |NativeType1, NativeType2: InitNativeFS(fsRes, flag, res)
  1721. END;
  1722. IF res = AosDisks.Ok THEN
  1723. IF f # NIL THEN
  1724. InitBootFile(disk.device, partition, f, res);
  1725. IF res = AosDisks.Ok THEN
  1726. info.String("Bootfile "); info.String(bootName); info.String(" written to disk"); info.Ln;
  1727. result.String(diskpartString); result.String(" has been successfully formatted.");
  1728. ELSE
  1729. GetErrorMsg("InitBootfile",res, error); ReportError(error);
  1730. result.String(diskpartString); result.String(" has been formatted but boot initialization failed");
  1731. END
  1732. ELSE
  1733. IF bootName # "" THEN
  1734. error := "Bootfile "; Strings.Append(error, bootName); Strings.Append(error, " missing - partition not bootable");
  1735. ReportError(error);
  1736. END;
  1737. result.String(diskpartString); result.String(" has been formatted but bootfile not found");
  1738. END
  1739. ELSE (* skip - error message already written *)
  1740. END
  1741. ELSE error := ""; Strings.Append(error, name); Strings.Append(error, "missing"); ReportError(error);
  1742. END
  1743. ELSE ReportError("Disk has errors");
  1744. END
  1745. ELSE ReportError("To reformat this partition, execute Partitions.Unsafe and try again");
  1746. END;
  1747. END DoOperation;
  1748. (* Initialize the Aos file system in a partition. See AosFiles.Volume.Init *)
  1749. PROCEDURE InitAosFS(fsres, flag: LONGINT; VAR res: WORD);
  1750. VAR fssize, i, j, ofs, size, x, fsofs: LONGINT; b: ARRAY BS OF CHAR; temp: ARRAY 256 OF CHAR;
  1751. BEGIN
  1752. ofs := dev.table[partition].start; size := dev.table[partition].size;
  1753. ASSERT(dev.blockSize = BS);
  1754. fsofs := fsres + BootLoaderSize + 4;
  1755. ASSERT((fsofs >= BootLoaderSize+4) & (fsofs <= size));
  1756. fssize := (size-fsofs) DIV AosBPS;
  1757. ASSERT(fssize > MinPartSize);
  1758. InitOBL(flag, res);
  1759. IF res = AosDisks.Ok THEN
  1760. dev.Transfer(AosDisks.Read, ofs, 1, b, 0, res);
  1761. IF res = AosDisks.Ok THEN (* init AosFS table *)
  1762. ASSERT((b[1FEH] = 55X) & (b[1FFH] = 0AAX));
  1763. Put4(b, 1F0H, fsofs); Put4(b, 1F4H, fssize); Put4(b, 1F8H, FSID);
  1764. b[1FCH] := CHR(FSVer); b[1FDH] := CHR(AosSSLog2);
  1765. dev.Transfer(AosDisks.Write, ofs, 1, b, 0, res);
  1766. IF res = AosDisks.Ok THEN
  1767. i := 0;
  1768. WHILE (i # AosBPS) & (res = AosDisks.Ok) DO
  1769. FOR j := 0 TO BS-1 DO b[j] := 0X END;
  1770. IF i = 0 THEN
  1771. b[0] := CHR(AosDirMark MOD 100H);
  1772. b[1] := CHR(AosDirMark DIV 100H MOD 100H);
  1773. b[2] := CHR(AosDirMark DIV 10000H MOD 100H);
  1774. b[3] := CHR(AosDirMark DIV 1000000H MOD 100H)
  1775. END;
  1776. x := ofs + fsofs + i; dev.Transfer(AosDisks.Write, x, 1, b, 0, res);
  1777. IF res # AosDisks.Ok THEN GetTransferError(dev, AosDisks.Write, x, res, temp); ReportError(temp) END;
  1778. INC(i)
  1779. END;
  1780. IF res = AosDisks.Ok THEN (* invalidate map *)
  1781. FOR j := 0 TO BS-1 DO b[j] := 0X END;
  1782. x := ofs + fsofs + (fssize-1)*AosBPS; dev.Transfer(AosDisks.Write, x, 1, b, 0, res);
  1783. IF res # AosDisks.Ok THEN GetTransferError(dev, AosDisks.Write, x, res, temp); ReportError(temp); END
  1784. END
  1785. ELSE GetTransferError(dev, AosDisks.Write, ofs, res, temp); ReportError(temp);
  1786. END
  1787. ELSE GetTransferError(dev, AosDisks.Read, ofs, res, temp); ReportError(temp);
  1788. END
  1789. ELSE GetErrorMsg("InitOBL failed: ", res, temp); ReportError(temp);
  1790. END;
  1791. END InitAosFS;
  1792. (* Initialize the Native file system in a partition. *)
  1793. PROCEDURE InitNativeFS(fsres, flag: LONGINT; VAR res: WORD);
  1794. VAR ofs, size, fssize, fsofs, startfs, i: LONGINT; b: ARRAY N2KSS*2 OF CHAR; temp: ARRAY 256 OF CHAR;
  1795. BEGIN
  1796. ofs := dev.table[partition].start; size := dev.table[partition].size;
  1797. ASSERT(dev.blockSize = BS);
  1798. fsofs := fsres + BootLoaderSize+4;
  1799. ASSERT((fsofs >= BootLoaderSize+4) & (fsofs <= size));
  1800. fssize := (size-fsofs) DIV N2KBPS;
  1801. ASSERT(fssize > MinPartSize);
  1802. InitOBL(flag, res);
  1803. IF res # AosDisks.Ok THEN
  1804. GetErrorMsg("InitLoader: ", res, temp); ReportError(temp);
  1805. ELSE
  1806. dev.Transfer(AosDisks.Read, dev.table[partition].start, 1, b, 0, res);
  1807. IF res # AosDisks.Ok THEN
  1808. GetTransferError(dev, AosDisks.Read, dev.table[partition].start, res, temp); ReportError(temp);
  1809. ELSE
  1810. ASSERT((b[1FEH] = 55X) & (b[1FFH] = 0AAX));
  1811. Put2(b, 0EH, fsofs); (* reserved *)
  1812. dev.Transfer(AosDisks.Write, dev.table[partition].start, 1, b, 0, res); (* update reserved *)
  1813. IF res # AosDisks.Ok THEN
  1814. GetTransferError(dev, AosDisks.Read, dev.table[partition].start, res, temp); ReportError(temp);
  1815. ELSE
  1816. FOR i := 0 TO N2KSS*2-1 DO b[i] := 0X END;
  1817. Put4(b, 0, N2KDirMark);
  1818. startfs := dev.table[partition].start + fsofs;
  1819. dev.Transfer(AosDisks.Write, startfs, N2KBPS*2, b, 0, res);
  1820. IF res = AosDisks.Ok THEN
  1821. Put4(b, 0, 0); (* invalidate map mark *)
  1822. dev.Transfer(AosDisks.Write, startfs + (fssize-1)*N2KBPS, N2KBPS, b, 0, res);
  1823. IF res # AosDisks.Ok THEN GetTransferError(dev, AosDisks.Write, startfs + (fssize-1)*N2KBPS, res, temp); ReportError(temp); END;
  1824. ELSE
  1825. GetTransferError(dev, AosDisks.Write, startfs, res, temp); ReportError(temp);
  1826. END
  1827. END
  1828. END
  1829. END;
  1830. END InitNativeFS;
  1831. (* Write the OBL boot loader and an empty config table to disk. *)
  1832. PROCEDURE InitOBL(flag: LONGINT; VAR res: WORD);
  1833. VAR
  1834. buf: ARRAY 10*BS OF CHAR; i, tsize, rsize, lsize, len : LONGINT;
  1835. f: Files.File; r: Files.Reader;
  1836. BEGIN
  1837. ASSERT(dev.blockSize = BS);
  1838. IF disk.gres = AosDisks.Ok THEN
  1839. f := Files.Old(BootLoaderName); NEW(r, f, 0);
  1840. ASSERT((f # NIL) & (f.Length() <= BootLoaderSize*BS)); (* assume boot file is present and small enough *)
  1841. len := f.Length();
  1842. r.Bytes(buf, 0, BootLoaderSize*BS, len);
  1843. ASSERT(r.res = 0);
  1844. ASSERT(Get4(buf, 1F8H) = FSIDOBL); (* new OBL.Bin *)
  1845. (* get parameters from boot loader *)
  1846. rsize := Get2(buf, 0EH); tsize := ORD(buf[10H]);
  1847. ASSERT((rsize-tsize)*BS = f.Length()); (* check boot loader size *)
  1848. lsize := f.Length() DIV BS;
  1849. ASSERT(lsize = BootLoaderSize);
  1850. (* set parameters in boot loader *)
  1851. IF (disk.size > DisketteLimit) THEN (* Windows 2000 workaround *)
  1852. Put2(buf, 0BH, 0); (* bytes per sector *)
  1853. buf[0DH] := 0X; (* sectors per cluster *)
  1854. Put2(buf, 11H, 0); (* root directory size *)
  1855. buf[15H] := 0X; (* media type *)
  1856. Put2(buf, 16H, 0) (* sectors per FAT *)
  1857. END;
  1858. IF dev.table[partition].size < 10000H THEN
  1859. Put2(buf, 13H, dev.table[partition].size)
  1860. ELSE
  1861. Put2(buf, 13H, 0)
  1862. END;
  1863. Put4(buf, 20H, dev.table[partition].size);
  1864. Put2(buf, 18H, disk.geo.spt);
  1865. Put2(buf, 1AH, disk.geo.hds);
  1866. Put4(buf, 1CH, dev.table[partition].start); (* boot sector *)
  1867. buf[24H] := GetDriveNum(dev); (* drive *)
  1868. buf[0AH] := CHR(flag); (* flag *)
  1869. (* now write the boot loader to disk *)
  1870. dev.Transfer(AosDisks.Write, dev.table[partition].start, lsize, buf, 0, res);
  1871. IF res = AosDisks.Ok THEN (* write an empty table *)
  1872. info.String("Boot loader "); info.String(BootLoaderName); info.String(" written"); info.Ln;
  1873. FOR i := 0 TO BS-1 DO buf[i] := 0FFX END;
  1874. i := 0;
  1875. WHILE (i < tsize) & (res = AosDisks.Ok) DO
  1876. dev.Transfer(AosDisks.Write, dev.table[partition].start + lsize + i, 1, buf, 0, res);
  1877. INC(i)
  1878. END
  1879. END
  1880. END
  1881. END InitOBL;
  1882. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  1883. BEGIN
  1884. Init^(disk, partition, out);
  1885. name := "Format"; desc := "Format partition"; dev := disk.device; locktype := WriterLock;
  1886. END Init;
  1887. END FormatPartition;
  1888. TYPE
  1889. ShowBlockCallback* = PROCEDURE {DELEGATE} (text : Texts.Text);
  1890. TYPE
  1891. ShowBlocks* = OBJECT(Operation);
  1892. VAR
  1893. (* parameters : dev#part block [numblocks] *)
  1894. block, numblocks : LONGINT;
  1895. callback : ShowBlockCallback;
  1896. (* Parameters: dev#part block [numblocks] *)
  1897. PROCEDURE SetParameters*(block, numblocks: LONGINT);
  1898. BEGIN
  1899. SELF.block := block; SELF.numblocks := numblocks;
  1900. END SetParameters;
  1901. PROCEDURE SetCallback*(callback : ShowBlockCallback);
  1902. BEGIN
  1903. SELF.callback := callback;
  1904. END SetCallback;
  1905. PROCEDURE ValidParameters*() : BOOLEAN;
  1906. BEGIN
  1907. IF block < 0 THEN
  1908. ReportError("Wrong parameter: block must be >= 0"); RETURN FALSE;
  1909. END;
  1910. IF numblocks < 0 THEN
  1911. ReportError("Wrong parameter: numblocks must be >= 1"); RETURN FALSE;
  1912. END;
  1913. IF (block + numblocks -1 > disk.table[partition].start + disk.table[partition].size) THEN
  1914. ReportError("Block not contained in this partition"); RETURN FALSE;
  1915. END;
  1916. RETURN TRUE;
  1917. END ValidParameters;
  1918. PROCEDURE DoOperation*; (** dev#part block [numblocks] ~ *)
  1919. VAR
  1920. text : Texts.Text; tw : TextUtilities.TextWriter;
  1921. pos, num : LONGINT; res : WORD;
  1922. buf: POINTER TO ARRAY OF CHAR;
  1923. temp: ARRAY 256 OF CHAR;
  1924. BEGIN
  1925. pos := disk.table[partition].start + block; num := numblocks;
  1926. NEW(text); NEW(tw, text); tw.SetFontName("Courier");
  1927. NEW(buf, disk.device.blockSize);
  1928. LOOP
  1929. IF num <= 0 THEN EXIT END;
  1930. ASSERT((disk.table[partition].start <= pos) & (pos <= disk.table[partition].start + disk.table[partition].size - 1));
  1931. disk.device.Transfer(AosDisks.Read, pos, 1, buf^, 0, res);
  1932. IF res # AosDisks.Ok THEN GetTransferError(disk.device,AosDisks.Read, pos, res, temp); ReportError(temp); EXIT END;
  1933. tw.SetFontStyle({WMGraphics.FontBold});
  1934. tw.String(diskpartString); tw.Char(" "); tw.Int(pos, 1); tw.Ln;
  1935. tw.SetFontStyle({});
  1936. WriteHexDump(tw, buf^, 0, disk.device.blockSize, 0);
  1937. INC(pos); DEC(num);
  1938. IF ~alive THEN tw.String(" interrupted."); tw.Ln; EXIT; END;
  1939. END;
  1940. tw.Update;
  1941. IF callback#NIL THEN callback(text); END;
  1942. IF alive THEN result.String("succeeded"); END;
  1943. END DoOperation;
  1944. PROCEDURE WriteHexDump(w: Streams.Writer; CONST buf: ARRAY OF CHAR; ofs, size, base: LONGINT);
  1945. VAR i: LONGINT; ch: CHAR;
  1946. BEGIN
  1947. WHILE ofs < size DO
  1948. w.Hex(base + ofs, -8); w.String(": ");
  1949. FOR i := 0 TO 15 DO
  1950. IF ofs+i < size THEN w.Hex(ORD(buf[ofs+i]), -2); w.Char(" ");
  1951. ELSE w.String(" ")
  1952. END;
  1953. END;
  1954. w.Char(" ");
  1955. FOR i := 0 TO 15 DO
  1956. IF ofs+i < size THEN
  1957. ch := buf[ofs+i];
  1958. IF (ch < " ") OR (ch > 7EX) THEN ch := "." END
  1959. ELSE
  1960. ch := " "
  1961. END;
  1962. w.Char(ch);
  1963. END;
  1964. w.Ln;
  1965. INC(ofs, 16)
  1966. END
  1967. END WriteHexDump;
  1968. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  1969. BEGIN
  1970. Init^(disk, partition, out);
  1971. name := "ShowBlocks"; desc := "Show block(s) of partition"; locktype := ReaderLock;
  1972. END Init;
  1973. END ShowBlocks;
  1974. TYPE
  1975. (** Update the boot loader OBL in an existing AosFS partition, replacing it by the new BBL handling the Init string differently.
  1976. The BBL must imperatively have the same size, 4 blocks, as the OBL. The same BBL is applicable to all AosFS partitions. *)
  1977. UpdateBootLoader* = OBJECT(Operation);
  1978. VAR
  1979. (* parameters *)
  1980. bootloader : ARRAY 32 OF CHAR;
  1981. PROCEDURE ValidParameters*() : BOOLEAN;
  1982. VAR valid : BOOLEAN;
  1983. BEGIN
  1984. valid := FALSE;
  1985. IF disk.device.blockSize = BS THEN
  1986. IF IsNativeType(disk.table[partition].type) OR (disk.isDiskette) THEN
  1987. valid := TRUE;
  1988. ELSE ReportError("Partition must have Native/Aos type");
  1989. END;
  1990. ELSE ReportError("Blocksize not supported");
  1991. END;
  1992. RETURN valid;
  1993. END ValidParameters;
  1994. (** dev#part BootLoader ~ *)
  1995. PROCEDURE SetParameters*(CONST bootloader : ARRAY OF CHAR);
  1996. BEGIN
  1997. SELF.bootloader := ""; Strings.Append(SELF.bootloader, bootloader);
  1998. END SetParameters;
  1999. PROCEDURE DoOperation*;
  2000. VAR res: WORD; fs : LONGINT; string: ARRAY 256 OF CHAR; f : Files.File;
  2001. BEGIN
  2002. fs := DetectFS(disk.device, partition);
  2003. IF (fs = AosFS32) OR (fs = AosFS128) THEN
  2004. f := Files.Old(bootloader);
  2005. IF f # NIL THEN
  2006. UpdateOBL(f, res);
  2007. IF res = AosDisks.Ok THEN
  2008. info.String(bootloader); info.String(" written to disk"); info.Ln;
  2009. result.String(diskpartString); result.String(" updated successful");
  2010. ELSE
  2011. GetErrorMsg("UpdateOBL failed: ", res, string); ReportError(string);
  2012. END
  2013. ELSE
  2014. string := "Bootloader file"; Strings.Append(string, bootloader); Strings.Append(string, " not found");
  2015. ReportError(string);
  2016. END
  2017. ELSE
  2018. string := ""; Strings.Append(string, diskpartString); Strings.Append(string, " is not AosFS-formatted");
  2019. ReportError(string);
  2020. END;
  2021. END DoOperation;
  2022. (* Overwrite the existing boot loader with the new one, leaving critical data untouched. *)
  2023. PROCEDURE UpdateOBL(f: Files.File; VAR res: WORD);
  2024. CONST MaxSize = MaxBootLoaderSize*BS;
  2025. VAR
  2026. b, bnew: ARRAY MaxSize OF CHAR; i, tsize, rsize, lsize, len: LONGINT;
  2027. r: Files.Reader;
  2028. BEGIN
  2029. ASSERT(disk.device.blockSize = BS);
  2030. IF res = AosDisks.Ok THEN
  2031. NEW(r, f, 0);
  2032. ASSERT((f # NIL) & (f.Length() <= BootLoaderSize*BS)); (* assume boot file is present and small enough *)
  2033. r.Bytes(bnew, 0, f.Length(), len);
  2034. ASSERT(r.res = 0);
  2035. lsize := f.Length() DIV BS;
  2036. disk.device.Transfer(AosDisks.Read, disk.device.table[partition].start, lsize, b, 0, res);
  2037. ASSERT(Get4(b, 1F8H) = FSID); (* OBL.Bin signature 'AOS!' *)
  2038. (* get parameters from boot loader *)
  2039. rsize := Get2(b, 0EH); tsize := ORD(b[10H]);
  2040. ASSERT((rsize-tsize)*BS = f.Length()); (* check boot loader size *)
  2041. lsize := f.Length() DIV BS;
  2042. ASSERT(lsize = BootLoaderSize);
  2043. (* set parameters in boot loader *)
  2044. FOR i := 0H TO 2H DO b[i] := bnew[i] END;
  2045. (* Leave the data from 3H to 24H untouched: info on the partition position and size
  2046. i.e. the BPB or BIOS Parameter Block - see comments "OBL variables" *)
  2047. FOR i := 25H TO 1EFH DO b[i] := bnew[i] END;
  2048. (* Leave the data from 1F0H to 1FFH untouched: info on the file sytem
  2049. see comments "AosFS Table Format" *)
  2050. FOR i := 200H TO BootLoaderSize*BS-1 DO b[i] := bnew[i] END;
  2051. (* now write the boot loader back to disk *)
  2052. disk.device.Transfer(AosDisks.Write, disk.device.table[partition].start, lsize, b, 0, res);
  2053. (* The configuration table is left as is. It is up to the user to specify
  2054. a new Init string (3 hexadecimal characters) suitable for the graphic card. *)
  2055. END
  2056. END UpdateOBL;
  2057. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  2058. BEGIN
  2059. Init^(disk, partition, out);
  2060. name := "UpdateLoader"; desc := "Update Boot Loader on partition"; locktype := WriterLock;
  2061. END Init;
  2062. END UpdateBootLoader;
  2063. TYPE
  2064. UpdateBootFile* = OBJECT(Operation);
  2065. VAR
  2066. (* parameters *)
  2067. bootfilename : ARRAY 128 OF CHAR;
  2068. PROCEDURE SetParameters*(CONST bootfilename : ARRAY OF CHAR);
  2069. BEGIN
  2070. SELF.bootfilename := ""; Strings.Append(SELF.bootfilename, bootfilename);
  2071. END SetParameters;
  2072. PROCEDURE ValidParameters*() : BOOLEAN;
  2073. VAR valid : BOOLEAN;
  2074. BEGIN
  2075. valid := FALSE;
  2076. IF disk.device.blockSize = BS THEN
  2077. IF IsNativeType(disk.table[partition].type) OR (disk.isDiskette)THEN
  2078. valid := TRUE;
  2079. ELSE ReportError("Partition must have type Native/Aos");
  2080. END;
  2081. ELSE ReportError("Blocksize not supported");
  2082. END;
  2083. RETURN valid;
  2084. END ValidParameters;
  2085. (* Update the boot file in an existing Oberon partition. *)
  2086. PROCEDURE DoOperation*; (** dev#part [ BootFile ] ~ *)
  2087. VAR f : Files.File; res: WORD; fs : LONGINT; temp: ARRAY 256 OF CHAR;
  2088. BEGIN
  2089. fs := DetectFS(disk.device, partition);
  2090. IF (fs # UnknownFS) THEN
  2091. IF bootfilename = "" THEN bootfilename := BootFileName; END;
  2092. f := Files.Old(bootfilename);
  2093. IF f # NIL THEN
  2094. InitBootFile(disk.device, partition, f, res);
  2095. IF res = AosDisks.Ok THEN
  2096. result.String("Bootfile "); result.String(bootfilename); result.String(" written to "); result.String(diskpartString); result.Ln;
  2097. ELSE
  2098. GetErrorMsg("InitBootFile failed", res, temp); ReportError(temp);
  2099. END;
  2100. ELSE Strings.Append(bootfilename, " not found"); ReportError(bootfilename);
  2101. END;
  2102. ELSE ReportError("Partition is not Oberon-formatted");
  2103. END;
  2104. END DoOperation;
  2105. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  2106. BEGIN
  2107. Init^(disk, partition, out);
  2108. name := "UpdateBootFile"; desc := "Updates boot file on partition "; locktype := WriterLock;
  2109. END Init;
  2110. END UpdateBootFile;
  2111. TYPE
  2112. WriteMBR* = OBJECT(Operation);
  2113. VAR
  2114. (* parameters *)
  2115. filename : ARRAY 128 OF CHAR; (* file containing MBR code *)
  2116. preserveTable : BOOLEAN; (* if TRUE, the partition table is not altered *)
  2117. preserveSignature : BOOLEAN; (* if TRUE, the disk signature used by Windows Vista (Offset 1B8H-1BBH) is not altered *)
  2118. PROCEDURE ValidParameters*() : BOOLEAN;
  2119. VAR valid : BOOLEAN;
  2120. BEGIN
  2121. valid := FALSE;
  2122. IF disk.device.blockSize = BS THEN
  2123. IF partition = 0 THEN
  2124. valid := TRUE;
  2125. ELSE ReportError("Only partition 0 is valid");
  2126. END;
  2127. ELSE ReportError("Blocksize not supported");
  2128. END;
  2129. RETURN valid;
  2130. END ValidParameters;
  2131. (** dev#0 name ~ *)
  2132. PROCEDURE SetParameters*(CONST filename : ARRAY OF CHAR; preserveTable, preserveSignature : BOOLEAN);
  2133. BEGIN
  2134. SELF.filename := ""; Strings.Append(SELF.filename, filename);
  2135. SELF.preserveTable := preserveTable;
  2136. SELF.preserveSignature := preserveSignature;
  2137. END SetParameters;
  2138. PROCEDURE DoOperation*;
  2139. VAR
  2140. f: Files.File; r: Files.Reader; buf1, buf2: ARRAY BS OF CHAR;
  2141. string : ARRAY 256 OF CHAR;
  2142. res: WORD; len, i: LONGINT;
  2143. BEGIN
  2144. f := Files.Old(filename);
  2145. IF f # NIL THEN
  2146. NEW(r, f, 0);
  2147. r.Bytes(buf1, 0, BS, len);
  2148. IF (r.res = 0) & (buf1[01FEH] = 055X) & (buf1[01FFH] = 0AAX) & (f.Length() = BS) THEN
  2149. IF preserveTable OR preserveSignature THEN
  2150. disk.device.Transfer(AosDisks.Read, 0, 1, buf2, 0, res);
  2151. IF (res = AosDisks.Ok) THEN
  2152. IF preserveTable THEN (* copy partition table *)
  2153. FOR i := Slot1 TO 01FDH DO buf1[i] := buf2[i] END;
  2154. END;
  2155. IF preserveSignature THEN (* copy Windows Vista disk signature *)
  2156. FOR i := 01B8H TO 01BBH DO buf1[i] := buf2[i]; END;
  2157. END;
  2158. ELSE
  2159. GetErrorMsg("Could not load MBR", res, string); ReportError(string);
  2160. RETURN;
  2161. END
  2162. END;
  2163. IF ~preserveTable THEN (* empty partition table *)
  2164. FOR i := Slot1 TO 01FDH DO buf1[i] := 0X END;
  2165. END;
  2166. disk.device.Transfer(AosDisks.Write, 0, 1, buf1, 0, res);
  2167. IF res = AosDisks.Ok THEN
  2168. result.String(filename); result.String(" written to MBR");
  2169. ELSE
  2170. GetErrorMsg("Could not write MBR", res, string); ReportError(string);
  2171. result.String("Operation failed");
  2172. END;
  2173. ELSE
  2174. Strings.Append(string, filename); Strings.Append(string, " does not contain MBR");
  2175. ReportError(string);
  2176. END
  2177. ELSE
  2178. string := ""; Strings.Append(string, filename); Strings.Append(string, " not found");
  2179. ReportError(string);
  2180. END;
  2181. END DoOperation;
  2182. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  2183. BEGIN
  2184. Init^(disk, partition, out);
  2185. name := "WriteMBR"; desc := "Write MBR to disk"; locktype := WriterLock;
  2186. END Init;
  2187. END WriteMBR;
  2188. TYPE
  2189. GetConfig* = OBJECT(Operation);
  2190. VAR
  2191. table : ConfigString;
  2192. PROCEDURE ValidParameters*() : BOOLEAN;
  2193. BEGIN
  2194. IF disk.device.blockSize # BS THEN ReportError("Unsupported blocksize"); RETURN FALSE; END;
  2195. RETURN TRUE;
  2196. END ValidParameters;
  2197. PROCEDURE GetTable*() : ConfigString;
  2198. BEGIN
  2199. IF (StatusFinished IN state.status) & ~(StatusError IN state.status) THEN RETURN table END;
  2200. RETURN NIL;
  2201. END GetTable;
  2202. PROCEDURE DoOperation*;
  2203. VAR config : Configuration; fs : LONGINT; res : WORD; temp: ARRAY 256 OF CHAR;
  2204. BEGIN
  2205. fs := DetectFS(disk.device, partition);
  2206. IF (fs = AosFS32) OR (fs = AosFS128) THEN
  2207. NEW(config);
  2208. config.GetTable(disk.device, partition, res);
  2209. IF res = AosDisks.Ok THEN
  2210. table := config.table;
  2211. result.String("Config loaded from "); result.String(diskpartString);
  2212. ELSE GetErrorMsg("GetTable failed: ", res, temp); ReportError(temp);
  2213. END;
  2214. ELSE ReportError("Volume is not AosFS");
  2215. END;
  2216. END DoOperation;
  2217. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  2218. BEGIN
  2219. Init^(disk, partition, out);
  2220. name := "GetConfig"; desc := "Read config string to partition"; locktype := ReaderLock;
  2221. END Init;
  2222. END GetConfig;
  2223. TYPE
  2224. SetConfig* = OBJECT(Operation);
  2225. VAR
  2226. (* parameters *)
  2227. configString : Strings.String;
  2228. pos : LONGINT;
  2229. PROCEDURE ValidParameters*() : BOOLEAN;
  2230. BEGIN
  2231. IF disk.device.blockSize = BS THEN
  2232. RETURN TRUE;
  2233. ELSE ReportError("Blocksize not supported");
  2234. END;
  2235. RETURN FALSE;
  2236. END ValidParameters;
  2237. (** dev#part configString ~ *)
  2238. (** config string format: {key = "value"} *)
  2239. PROCEDURE SetParameters*(configString : Strings.String; pos : LONGINT);
  2240. BEGIN
  2241. SELF.configString := configString; SELF.pos := pos;
  2242. END SetParameters;
  2243. PROCEDURE DoOperation*;
  2244. VAR config : Configuration; fs, i : LONGINT; res : WORD; temp: ARRAY 256 OF CHAR;
  2245. BEGIN
  2246. fs := DetectFS(disk.device, partition);
  2247. IF (fs = AosFS32) OR (fs = AosFS128) THEN
  2248. NEW(config);
  2249. config.GetTable(disk.device, partition, res);
  2250. IF res = AosDisks.Ok THEN
  2251. LOOP
  2252. i := config.FindEntry(0, 8);
  2253. IF i < 0 THEN EXIT END;
  2254. config.DeleteEntry(i)
  2255. END;
  2256. IF config.ParseConfig(configString^, pos) THEN
  2257. config.PutTable(disk.device, partition, res);
  2258. IF res = AosDisks.Ok THEN
  2259. result.String("Config written to "); result.String(diskpartString);
  2260. ELSE GetErrorMsg("PutTable failed", res, temp); ReportError(temp);
  2261. END
  2262. ELSE ReportError("syntax error");
  2263. END;
  2264. ELSE GetErrorMsg("GetTable failed: ", res, temp); ReportError(temp);
  2265. END;
  2266. ELSE ReportError("Volume is not AosFS");
  2267. END;
  2268. END DoOperation;
  2269. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  2270. BEGIN
  2271. Init^(disk, partition, out);
  2272. name := "SetConfig"; desc := "Write config string to partition"; locktype := WriterLock;
  2273. END Init;
  2274. END SetConfig;
  2275. TYPE
  2276. ConfigEntry* = RECORD
  2277. key*, value* : Strings.String;
  2278. END;
  2279. Table* = POINTER TO ARRAY OF ConfigEntry;
  2280. ConfigTable* = OBJECT
  2281. VAR
  2282. entries : Table;
  2283. hex : ARRAY 32 OF CHAR;
  2284. PROCEDURE GetEntries*() : Table;
  2285. VAR table : Table; i : LONGINT;
  2286. BEGIN {EXCLUSIVE}
  2287. table := NIL;
  2288. IF (entries # NIL) THEN
  2289. NEW(table, LEN(entries));
  2290. FOR i := 0 TO LEN(entries)-1 DO
  2291. table[i] := entries[i];
  2292. END;
  2293. END;
  2294. RETURN table;
  2295. END GetEntries;
  2296. PROCEDURE GetNofEntries*() : LONGINT;
  2297. VAR len : LONGINT;
  2298. BEGIN {EXCLUSIVE}
  2299. IF (entries = NIL) THEN len := 0; ELSE len := LEN(entries); END;
  2300. RETURN len;
  2301. END GetNofEntries;
  2302. PROCEDURE GetAsString*() : Strings.String;
  2303. BEGIN {EXCLUSIVE}
  2304. RETURN GetAsStringInternal();
  2305. END GetAsString;
  2306. (** Replace all occurence of entries with key 'key'. If no entry is found, add it *)
  2307. PROCEDURE SetValueOf*(key, value : Strings.String);
  2308. VAR entry : ConfigEntry; found : BOOLEAN; i : LONGINT;
  2309. BEGIN {EXCLUSIVE}
  2310. ASSERT((key # NIL) & (value # NIL));
  2311. entry.key := key; entry.value := value;
  2312. IF (entries = NIL) THEN
  2313. NEW(entries, 1);
  2314. entries[i] := entry;
  2315. ELSE
  2316. found := FALSE;
  2317. FOR i := 0 TO LEN(entries)-1 DO
  2318. IF (entries[i].key^ = key^) THEN
  2319. entries[i] := entry;
  2320. found := TRUE;
  2321. END;
  2322. END;
  2323. IF ~found THEN
  2324. AddEntryInternal(0, entry);
  2325. END;
  2326. END;
  2327. END SetValueOf;
  2328. PROCEDURE GetAsStringInternal() : Strings.String;
  2329. VAR string : ARRAY MaxConfigString OF CHAR; w : Streams.StringWriter; i : LONGINT;
  2330. BEGIN
  2331. NEW(w, MaxConfigString);
  2332. FOR i := 0 TO LEN(entries)-1 DO
  2333. w.String(entries[i].key^); w.String(" = "); w.Char(22X); w.String(entries[i].value^); w.Char(22X); w.Ln;
  2334. IF w.res # Streams.Ok THEN
  2335. RETURN NIL;
  2336. END;
  2337. END;
  2338. w.Char("~"); w.Get(string);
  2339. RETURN Strings.NewString(string);
  2340. END GetAsStringInternal;
  2341. PROCEDURE LoadFromStream*(r : Streams.Reader; VAR msg : ARRAY OF CHAR; VAR res : WORD);
  2342. BEGIN {EXCLUSIVE}
  2343. ASSERT(r # NIL);
  2344. entries := NIL; res := Ok; msg := "";
  2345. IF ~ParseStream(r) THEN
  2346. entries := NIL;
  2347. res := 99; COPY("Configuration string parsing failed", msg);
  2348. END;
  2349. END LoadFromStream;
  2350. (** Load configuration data from file *)
  2351. PROCEDURE LoadFromFile*(CONST filename : ARRAY OF CHAR; VAR msg : ARRAY OF CHAR; VAR res : WORD);
  2352. VAR file : Files.File; r: Files.Reader;
  2353. BEGIN {EXCLUSIVE}
  2354. res := Ok; entries := NIL;
  2355. file := Files.Old(filename);
  2356. IF (file # NIL) THEN
  2357. NEW(r, file, 0);
  2358. IF ParseStream(r) THEN
  2359. res := Ok; msg := "";
  2360. ELSE
  2361. entries := NIL;
  2362. msg := "Parsing configuration file "; Strings.Append(msg, filename); Strings.Append(msg, " failed");
  2363. res := 99;
  2364. END;
  2365. ELSE
  2366. msg := "Configuration file "; Strings.Append(msg, filename); Strings.Append(msg, " not found");
  2367. res := 99;
  2368. END;
  2369. END LoadFromFile;
  2370. (** Store configuration data to file *)
  2371. PROCEDURE StoreToFile*(CONST filename : ARRAY OF CHAR; VAR msg : ARRAY OF CHAR; VAR res : WORD);
  2372. VAR f : Files.File; w : Files.Writer; string : Strings.String;
  2373. BEGIN {EXCLUSIVE}
  2374. res := Ok;
  2375. IF (entries # NIL) THEN
  2376. string := GetAsStringInternal();
  2377. ASSERT(string # NIL);
  2378. f := Files.New(filename);
  2379. IF (f # NIL) THEN
  2380. Files.OpenWriter(w, f, 0); w.String(string^); w.Update;
  2381. IF w.res = Streams.Ok THEN
  2382. Files.Register(f); f.Update;
  2383. msg := ""; res := Ok;
  2384. ELSE msg := "Error when writing to file "; Strings.Append(msg, filename); res := 99;
  2385. END;
  2386. ELSE msg := "Could not create file "; Strings.Append(msg, filename); res := 99;
  2387. END;
  2388. ELSE msg := "No configuration data available to store"; res := 99;
  2389. END;
  2390. END StoreToFile;
  2391. (* builds up configTable from config.table*)
  2392. PROCEDURE ParseRawTable*(config : Configuration);
  2393. VAR
  2394. entry : ConfigEntry;
  2395. key, value : ARRAY MaxStringLength OF CHAR;
  2396. ch : CHAR;
  2397. i, j, pos : LONGINT;
  2398. BEGIN {EXCLUSIVE}
  2399. entries := NIL;
  2400. IF config.table # NIL THEN
  2401. i := config.FindEntry(0, 8);
  2402. IF i >= 0 THEN
  2403. pos := 0;
  2404. INC(i, 8);
  2405. WHILE config.table[i] # 0X DO
  2406. key := ""; j := 0;
  2407. REPEAT (* get key *)
  2408. key[j] := config.table[i]; INC(i); INC(j);
  2409. UNTIL config.table[i] = 0X;
  2410. key[j] := 0X;
  2411. entry.key := Strings.NewString(key);
  2412. value := ""; j := 0;
  2413. LOOP (* get value *)
  2414. INC(i); ch := config.table[i];
  2415. IF ch = 0X THEN EXIT END;
  2416. IF (ch >= " ") & (ch < 7FX) THEN
  2417. value[j] := config.table[i];
  2418. ELSE
  2419. value[j] := "%"; INC(j);
  2420. value[j] := hex[ORD(ch) DIV 10H]; INC(j);
  2421. value[j] := hex[ORD(ch) MOD 10H];
  2422. END;
  2423. INC(j);
  2424. END;
  2425. value[j] := 0X;
  2426. entry.value := Strings.NewString(value);
  2427. AddEntryInternal(pos, entry); (* append entry *)
  2428. INC(i); INC(pos);
  2429. END
  2430. END;
  2431. END;
  2432. END ParseRawTable;
  2433. PROCEDURE ParseStream(r : Streams.Reader) : BOOLEAN;
  2434. VAR
  2435. temp : ARRAY 1024 OF CHAR; ch : CHAR;
  2436. entry : ConfigEntry;
  2437. error : BOOLEAN;
  2438. i : LONGINT;
  2439. BEGIN
  2440. ASSERT(r # NIL);
  2441. entries := NIL;
  2442. error := FALSE; i := 0;
  2443. LOOP
  2444. r.SkipWhitespace;
  2445. ch := r.Peek();
  2446. IF ch = "~" THEN (* end of configuration string *)
  2447. EXIT;
  2448. END;
  2449. (* read key *)
  2450. r.String(temp);
  2451. IF r.res = Streams.Ok THEN
  2452. Strings.Trim(temp, " ");
  2453. entry.key := Strings.NewString(temp);
  2454. ELSE error := TRUE; EXIT;
  2455. END;
  2456. r.SkipWhitespace;
  2457. r.Char(ch);
  2458. IF (r.res # Streams.Ok) OR (ch # "=") THEN error := TRUE; EXIT; END;
  2459. r.SkipWhitespace;
  2460. (* read value *)
  2461. r.String(temp);
  2462. IF r.res = Streams.Ok THEN
  2463. Strings.Trim(temp, " ");
  2464. entry.value := Strings.NewString(temp);
  2465. ELSE error := TRUE; EXIT;
  2466. END;
  2467. r.SkipSpaces;
  2468. IF ~r.EOLN() THEN error := TRUE; EXIT; END;
  2469. AddEntryInternal(i, entry); INC(i);
  2470. END;
  2471. IF error THEN entries := NIL; END;
  2472. RETURN ~error;
  2473. END ParseStream;
  2474. PROCEDURE ChangeEntry*(pos : LONGINT; key, value : Strings.String);
  2475. BEGIN {EXCLUSIVE}
  2476. IF (pos >= 0) & (pos < LEN(entries)) THEN
  2477. entries[pos].key := key;
  2478. entries[pos].value := value;
  2479. END;
  2480. END ChangeEntry;
  2481. PROCEDURE AddEntry*(pos : LONGINT; entry : ConfigEntry);
  2482. BEGIN {EXCLUSIVE}
  2483. AddEntryInternal(pos, entry);
  2484. END AddEntry;
  2485. PROCEDURE AddEntryInternal(pos : LONGINT; entry : ConfigEntry);
  2486. VAR newTable : Table; i, j : LONGINT;
  2487. BEGIN
  2488. ASSERT(pos >=0);
  2489. IF entries = NIL THEN
  2490. NEW(entries, 1); entries[0] := entry;
  2491. ELSE
  2492. ASSERT(pos < LEN(entries)+1);
  2493. NEW(newTable, LEN(entries)+1);
  2494. i := 0; j := 0;
  2495. LOOP
  2496. IF i = pos THEN
  2497. newTable[i] := entry;
  2498. ELSE
  2499. newTable[i] := entries[j];
  2500. INC(j);
  2501. END;
  2502. INC(i); IF i >= LEN(newTable) THEN EXIT END;
  2503. END;
  2504. entries := newTable;
  2505. END;
  2506. END AddEntryInternal;
  2507. PROCEDURE RemoveEntry*(entry : LONGINT);
  2508. BEGIN {EXCLUSIVE}
  2509. RemoveEntryInternal(entry);
  2510. END RemoveEntry;
  2511. PROCEDURE RemoveEntryInternal(entry : LONGINT);
  2512. VAR newTable : Table; i, j : LONGINT;
  2513. BEGIN
  2514. IF (entries # NIL) THEN
  2515. IF LEN(entries) = 1 THEN
  2516. entries := NIL;
  2517. ELSE
  2518. NEW(newTable, LEN(entries) -1);
  2519. j := 0;
  2520. FOR i := 0 TO LEN(entries) - 1 DO
  2521. IF i # entry THEN
  2522. newTable[j] := entries[i]; INC(j);
  2523. END;
  2524. END;
  2525. entries := newTable;
  2526. END;
  2527. END;
  2528. END RemoveEntryInternal;
  2529. PROCEDURE SwapEntries*(i, j : LONGINT);
  2530. VAR temp : ConfigEntry;
  2531. BEGIN {EXCLUSIVE}
  2532. IF (i >= 0) & (i < LEN(entries)) & (j >= 0) & (j < LEN(entries)) THEN
  2533. temp := entries[i];
  2534. entries[i] := entries[j];
  2535. entries[j] := temp;
  2536. END;
  2537. END SwapEntries;
  2538. PROCEDURE Clone*() : ConfigTable;
  2539. VAR configTable : ConfigTable;
  2540. BEGIN
  2541. NEW(configTable);
  2542. configTable.entries := GetEntries();
  2543. RETURN configTable;
  2544. END Clone;
  2545. END ConfigTable;
  2546. TYPE
  2547. ConfigString* = POINTER TO ARRAY OF CHAR;
  2548. Configuration* = OBJECT
  2549. VAR
  2550. table* : ConfigString; (* in raw format *)
  2551. hex : ARRAY 32 OF CHAR;
  2552. (* Read the config table from the specified partition. *)
  2553. PROCEDURE GetTable*(dev: AosDisks.Device; part: LONGINT; VAR res: WORD);
  2554. VAR tsize, reserved, fsOfs: LONGINT;
  2555. BEGIN {EXCLUSIVE}
  2556. table := NIL; GetVars(dev, part, tsize, reserved, fsOfs, res);
  2557. BootLoaderSize := reserved-tsize;
  2558. (*TRACE(BootLoaderSize);*)
  2559. IF res = AosDisks.Ok THEN
  2560. NEW(table, tsize*BS);
  2561. dev.Transfer(AosDisks.Read, dev.table[part].start + BootLoaderSize, tsize, table^, 0, res)
  2562. END
  2563. END GetTable;
  2564. (* Overwrite the config table on the specified partition. *)
  2565. PROCEDURE PutTable*(dev: AosDisks.Device; part: LONGINT; VAR res: WORD);
  2566. VAR tsize, reserved, fsOfs: LONGINT;
  2567. BEGIN {EXCLUSIVE}
  2568. GetVars(dev, part, tsize, reserved, fsOfs, res);
  2569. BootLoaderSize := reserved-tsize;
  2570. (*TRACE(BootLoaderSize);*)
  2571. IF res = AosDisks.Ok THEN
  2572. ASSERT(tsize*BS = LEN(table^)); (* same size *)
  2573. dev.Transfer(AosDisks.Write, dev.table[part].start + BootLoaderSize, tsize, table^, 0, res)
  2574. END
  2575. END PutTable;
  2576. (* Parse the configuration strings on the command line and add them to the config table. *)
  2577. PROCEDURE ParseConfig*(CONST table : ARRAY OF CHAR; pos : LONGINT): BOOLEAN;
  2578. CONST CR = 0DX; LF = 0AX;
  2579. VAR config: ARRAY MaxConfig OF CHAR; result : BOOLEAN; i, j: LONGINT;
  2580. BEGIN
  2581. ASSERT((pos >= 0) & (pos < LEN(table)));
  2582. i := 0; j := pos;
  2583. LOOP
  2584. (* skip whitespace and comment lines *)
  2585. REPEAT
  2586. WHILE (j < LEN(table)) & (table[j] <= " ") DO INC(j); END;
  2587. IF (j < LEN(table)) & (table[j] = "#") THEN (* comment; skip line *)
  2588. WHILE (j < LEN(table)) & (table[j] # CR) & (table[j] # LF) DO INC(j); END;
  2589. END;
  2590. IF j >= LEN(table) THEN result := FALSE; EXIT END;
  2591. UNTIL (table[j] # CR) & (table[j] # LF);
  2592. IF table[j] = "~" THEN (* end of config table *)
  2593. config[i] := 0X; INC(i);
  2594. UnQuote(config, i);
  2595. AddEntry(8, i, config);
  2596. result := TRUE;
  2597. EXIT
  2598. END;
  2599. (* read key *)
  2600. REPEAT
  2601. config[i] := table[j];
  2602. INC(i); INC(j);
  2603. UNTIL (j >= LEN(table)) OR (table[j] <= " ") OR (table[j] = "=") OR (table[j] = 22X);
  2604. (* skip whitespace *)
  2605. WHILE (j < LEN(table)) & (table[j] > 0X) & (table[j] <= " ") DO INC(j); END;
  2606. (* exspected character: "=" *)
  2607. IF (j >= LEN(table)) OR (table[j] # "=") THEN result := FALSE; EXIT END;
  2608. config[i] := 0X; INC(i); INC(j);
  2609. (* skip whitespace *)
  2610. WHILE (j < LEN(table)) & (table[j] > 0X) & (table[j] <= " ") DO INC(j); END;
  2611. (* expecting opening quote *)
  2612. IF (j >= LEN(table)) OR (table[j] # 22X) THEN result := FALSE; EXIT END;
  2613. (* read value *)
  2614. INC(j); WHILE (j < LEN(table)) & (table[j] # 22X) & (table[j] >= " ") DO config[i] := table[j]; INC(i); INC(j); END;
  2615. (* exspecting closing quote *)
  2616. IF (j >= LEN(table)) OR (table[j] # 22X) THEN result := FALSE; EXIT END;
  2617. config[i] := 0X; INC(i); INC(j);
  2618. END;
  2619. RETURN result;
  2620. END ParseConfig;
  2621. (* Parse the table and return it as string *)
  2622. PROCEDURE GetTableAsString*() : Streams.StringWriter;
  2623. CONST MaxSize = 2048;
  2624. VAR w : Streams.StringWriter; i: LONGINT; ch: CHAR;
  2625. BEGIN
  2626. NEW(w, MaxSize);
  2627. IF table # NIL THEN
  2628. i := FindEntry(0, 8);
  2629. IF i >= 0 THEN
  2630. INC(i, 8);
  2631. WHILE table[i] # 0X DO
  2632. w.String(" ");
  2633. REPEAT w.Char(table[i]); INC(i) UNTIL table[i] = 0X;
  2634. w.Char("="); w.Char(22X);
  2635. LOOP
  2636. INC(i); ch := table[i];
  2637. IF ch = 0X THEN EXIT END;
  2638. IF ch = ";" THEN ch := ","; END; (* ";" is used to separate Commands *)
  2639. IF (ch >= " ") & (ch < 7FX) THEN
  2640. w.Char(ch);
  2641. ELSE
  2642. w.Char("%"); w.Char( hex[ORD(ch) DIV 10H]); w.Char(hex[ORD(ch) MOD 10H]);
  2643. END
  2644. END;
  2645. w.Char(22X); w.Ln;
  2646. INC(i)
  2647. END
  2648. END;
  2649. w.Char("~")
  2650. ELSE w.String("GetTable: No configuration is loaded");
  2651. END;
  2652. w.Ln;
  2653. RETURN w;
  2654. END GetTableAsString;
  2655. (* Find the next occurance of the specified entry type in the config table. *)
  2656. PROCEDURE FindEntry*(i, type: LONGINT): LONGINT;
  2657. VAR t: LONGINT;
  2658. BEGIN (* caller must hold lock on object *)
  2659. ASSERT(table#NIL);
  2660. LOOP
  2661. t := Get4(table^, i);
  2662. IF t = type THEN RETURN i
  2663. ELSIF t = -1 THEN RETURN -1
  2664. ELSE INC(i, Get4(table^, i+4));
  2665. END;
  2666. IF i >= LEN(table) THEN RETURN -1 END;
  2667. END;
  2668. END FindEntry;
  2669. (* Add an entry to the end of the table. *)
  2670. PROCEDURE AddEntry*(type, dsize: LONGINT; CONST data: ARRAY OF CHAR);
  2671. VAR i, j, size: LONGINT;
  2672. BEGIN {EXCLUSIVE}
  2673. ASSERT(dsize >= 0);
  2674. i := FindEntry(0, -1); (* find end of table *)
  2675. size := (dsize+3) DIV 4 * 4 + 8;
  2676. Put4(table^, i, type); Put4(table^, i+4, size);
  2677. j := 0; WHILE j # dsize DO table[i+8+j] := data[j]; INC(j) END;
  2678. WHILE j MOD 4 # 0 DO table[i+8+j] := 0X; INC(j) END;
  2679. Put4(table^, i+size, -1)
  2680. END AddEntry;
  2681. (* Delete the specified entry. *)
  2682. PROCEDURE DeleteEntry*(i: LONGINT);
  2683. VAR j, s: LONGINT;
  2684. BEGIN {EXCLUSIVE}
  2685. ASSERT(Get4(table^, i) # -1); (* can not delete end marker *)
  2686. s := Get4(table^, i+4);
  2687. FOR j := i TO LEN(table^)-s-1 DO table[j] := table[j+s] END
  2688. END DeleteEntry;
  2689. PROCEDURE UnQuote(VAR config: ARRAY OF CHAR; VAR len: LONGINT);
  2690. VAR i, j: LONGINT;
  2691. BEGIN
  2692. i := 0;
  2693. WHILE i < len DO
  2694. IF (config[i] = "%") & IsHex(config[i+1]) & IsHex(config[i+2]) THEN
  2695. config[i] := CHR(HexVal(config[i+1])*10H + HexVal(config[i+2]));
  2696. ASSERT(config[i] # 0X);
  2697. FOR j := i+1 TO len-1 DO config[j] := config[j+2] END;
  2698. DEC(len, 2)
  2699. ELSE
  2700. INC(i)
  2701. END
  2702. END
  2703. END UnQuote;
  2704. PROCEDURE HexVal(ch: CHAR): LONGINT;
  2705. BEGIN
  2706. CASE ch OF
  2707. "0".."9": RETURN ORD(ch)-ORD("0")
  2708. |"A".."F": RETURN ORD(ch)-ORD("A")+10
  2709. |"a".."f": RETURN ORD(ch)-ORD("a")+10
  2710. END
  2711. END HexVal;
  2712. PROCEDURE IsHex(ch: CHAR): BOOLEAN;
  2713. BEGIN
  2714. RETURN (ch >= "0") & (ch <= "9") OR (CAP(ch) >= "A") & (CAP(ch) <= "F")
  2715. END IsHex;
  2716. PROCEDURE &Init*;
  2717. BEGIN
  2718. hex := "0123456789ABCDEF";
  2719. END Init;
  2720. END Configuration;
  2721. TYPE
  2722. (* Change the type of dev#part from oldtype to newtype *)
  2723. ChangePartType* = OBJECT(Operation)
  2724. VAR
  2725. (* parameters *)
  2726. oldtype, newtype : LONGINT;
  2727. (* dev#name oldtype newtime *)
  2728. PROCEDURE SetParameters*(oldtype, newtype : LONGINT);
  2729. BEGIN
  2730. SELF.oldtype := oldtype; SELF.newtype := newtype;
  2731. END SetParameters;
  2732. PROCEDURE ValidParameters*() : BOOLEAN;
  2733. VAR valid : BOOLEAN;
  2734. BEGIN
  2735. valid := FALSE;
  2736. IF disk.table[partition].type # oldtype THEN
  2737. ReportError("Selected Partition has not type oldtype");
  2738. ELSIF disk.table[partition].flags * {AosDisks.Valid} = {} THEN
  2739. ReportError("Partition must be valid");
  2740. ELSIF (newtype <= 0) OR (newtype > 255) THEN
  2741. ReportError("The new type must be in [1,255]");
  2742. ELSE (* parameters valid *)
  2743. valid := TRUE;
  2744. END;
  2745. RETURN valid;
  2746. END ValidParameters;
  2747. (* Change type of partition from oldtype to newtype *)
  2748. PROCEDURE DoOperation*;
  2749. VAR
  2750. b: ARRAY BS OF CHAR; e: LONGINT; res : WORD; temp: ARRAY 256 OF CHAR;
  2751. BEGIN
  2752. ASSERT(disk.table[partition].type = oldtype);
  2753. ASSERT(disk.device.blockSize = BS);
  2754. SetStatus(state.status, "Changing type", 0, 0, 0, FALSE);
  2755. disk.device.Transfer(AosDisks.Read, disk.table[partition].ptblock, 1, b, 0, res);
  2756. IF res = AosDisks.Ok THEN
  2757. e := disk.table[partition].ptoffset;
  2758. ASSERT((e >= Slot1) & (e <= Slot4)); (* too strict, but good for now *)
  2759. ASSERT((ORD(b[e+4]) = oldtype) & (b[510] = 055X) & (b[511] = 0AAX));
  2760. ASSERT((newtype > 0) & (newtype < 256));
  2761. b[e+4] := CHR(newtype);
  2762. disk.device.Transfer(AosDisks.Write, disk.table[partition].ptblock, 1, b, 0, res);
  2763. IF res = AosDisks.Ok THEN
  2764. disk.table[partition].type := newtype;
  2765. result.String("Changed type of "); result.String(diskpartString); result.String(" from ");
  2766. result.Int(oldtype, 0); result.String(" to "); result.Int(newtype, 0);
  2767. ELSE GetTransferError(disk.device, AosDisks.Read, disk.table[partition].ptblock, res, temp); ReportError(temp);
  2768. END
  2769. ELSE GetTransferError(disk.device, AosDisks.Read, disk.table[partition].ptblock, res, temp); ReportError(temp);
  2770. END;
  2771. END DoOperation;
  2772. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  2773. BEGIN
  2774. Init^(disk, partition, out);
  2775. name := "ChangeType"; desc := "Change type of partition"; locktype := WriterLock;
  2776. END Init;
  2777. END ChangePartType;
  2778. TYPE
  2779. (** Create a partition of the specified size and type. *)
  2780. CreatePartition* = OBJECT(Operation);
  2781. VAR
  2782. (* parameters *)
  2783. size, type : LONGINT;
  2784. override : BOOLEAN;
  2785. (** dev#part sizeMB ~ *)
  2786. PROCEDURE SetParameters*(size, type : LONGINT; override : BOOLEAN);
  2787. BEGIN
  2788. SELF.size := size; SELF.type := type; SELF.override := override;
  2789. END SetParameters;
  2790. PROCEDURE ValidParameters*() : BOOLEAN;
  2791. VAR temp: ARRAY 256 OF CHAR;
  2792. BEGIN
  2793. IF disk.device.blockSize # BS THEN ReportError("Blocksize # 512B not supported"); RETURN FALSE; END;
  2794. IF disk.gres # AosDisks.Ok THEN GetErrorMsg("Geometry error", disk.gres, temp); ReportError(temp); RETURN FALSE; END;
  2795. IF disk.geo.cyls * disk.geo.hds * disk.geo.spt < DisketteLimit THEN ReportError("Geormetry error"); RETURN FALSE; END;
  2796. IF (partition >= LEN(disk.table)) OR (disk.table[partition].type # FreeSpace) THEN
  2797. ReportError("Specified partition not free"); RETURN FALSE;
  2798. END;
  2799. IF AosDisks.Valid IN disk.table[partition].flags THEN ReportError("Selected partition is primary partition"); RETURN FALSE; END;
  2800. IF size < 0 THEN ReportError("Size parameter invalid"); RETURN FALSE; END;
  2801. RETURN TRUE;
  2802. END ValidParameters;
  2803. PROCEDURE DoOperation*;
  2804. VAR done : BOOLEAN;
  2805. BEGIN
  2806. IF (disk.device.openCount = 1) OR override THEN (* only "mounted" once, so ok to change names *)
  2807. IF (AosDisks.Primary IN disk.table[partition].flags) THEN
  2808. done := CreatePrimary(size*1024*(1024 DIV BS), type);
  2809. ELSE
  2810. done := CreateLogical(size*1024*(1024 DIV BS), type);
  2811. END;
  2812. IF done THEN result.String("Partition created"); END;
  2813. ELSE ReportError("Device has already been mounted");
  2814. END;
  2815. END DoOperation;
  2816. PROCEDURE CreatePrimary(size, type: LONGINT) : BOOLEAN;
  2817. VAR
  2818. mbr, epbr : Block;
  2819. i, e : LONGINT; res: WORD;
  2820. temp: ARRAY 256 OF CHAR;
  2821. BEGIN
  2822. ASSERT((disk.device.blockSize = BS) & (disk.table[partition].type = FreeSpace));
  2823. ASSERT(disk.table[partition].ptblock = 0); (* primary partition entry is in MBR *)
  2824. IF IsExtendedPartition(type) THEN (* at most one extended partition per disk is allowed *)
  2825. FOR i := 0 TO LEN(disk.device.table)-1 DO
  2826. IF (AosDisks.Valid IN disk.device.table[i].flags) & IsExtendedPartition(disk.device.table[i].type) THEN
  2827. ReportError("Create failed: There is already an extended partition on device");
  2828. RETURN FALSE;
  2829. END;
  2830. END;
  2831. END;
  2832. disk.device.Transfer(AosDisks.Read, 0, 1, mbr, 0, res);
  2833. IF res = AosDisks.Ok THEN
  2834. IF IsMBR(mbr) THEN
  2835. (* find first free slot *)
  2836. e := -1;
  2837. FOR i := 0 TO 3 DO
  2838. IF (e = -1) & (Get4(mbr, Slot1 + 16*i + 12) = 0) THEN (* size is 0 (empty slot) *)
  2839. e := Slot1 + 16*i
  2840. END
  2841. END;
  2842. IF e # -1 THEN (* found free slot *)
  2843. IF ~FillinSlot(disk, partition, mbr, e, type, disk.table[partition].start, size) THEN
  2844. ReportError("Could not create partition: Partition too small");
  2845. RETURN FALSE
  2846. END;
  2847. (* write the MBR *)
  2848. disk.device.Transfer(AosDisks.Write, 0, 1, mbr, 0, res);
  2849. IF res = AosDisks.Ok THEN
  2850. IF IsExtendedPartition(type) THEN (* write emtpy EPBR *)
  2851. FOR i := 0 TO BS-1 DO epbr[i] := 0X; END;
  2852. epbr[510] := 055X; epbr[511] := 0AAX; (* EPBR signature *)
  2853. disk.device.Transfer(AosDisks.Write, disk.table[partition].start, 1, epbr, 0 , res);
  2854. IF res # AosDisks.Ok THEN
  2855. GetErrorMsg("Critical: Failed to write EPBR", res, temp); ReportError(temp);
  2856. RETURN FALSE;
  2857. END;
  2858. END;
  2859. RETURN TRUE;
  2860. ELSE GetErrorMsg("Critical: Failed to write MBR", res, temp); ReportError(temp);
  2861. END;
  2862. ELSE ReportError("Can't create partition: No free slots");
  2863. END
  2864. ELSE ReportError("Can't create partition: MBR signature wrong");
  2865. END;
  2866. ELSE GetErrorMsg("Can't create partition: Couldn't load MBR", res, temp); ReportError(temp);
  2867. END;
  2868. RETURN FALSE;
  2869. END CreatePrimary;
  2870. PROCEDURE CreateLogical(size, type: LONGINT) : BOOLEAN;
  2871. CONST
  2872. TypeExt = 5;
  2873. VAR
  2874. epbr, new : Block;
  2875. slot1, slot2 : ARRAY 16 OF CHAR;
  2876. extStart, extPart, lastLogical, i: LONGINT; res: WORD;
  2877. BEGIN
  2878. ASSERT((disk.device.blockSize = BS) & (disk.table[partition].type = FreeSpace));
  2879. IF IsExtendedPartition(type) THEN
  2880. ReportError("Can't create extended partition in extended partition");
  2881. RETURN FALSE;
  2882. END;
  2883. (* we need the start sector of the extended partition that will contain the logical drive *)
  2884. extStart := 0;
  2885. FOR i := 0 TO LEN(disk.device.table)-1 DO
  2886. IF IsExtendedPartition(disk.device.table[i].type) THEN
  2887. IF extStart = 0 THEN
  2888. extStart := disk.device.table[i].start; extPart := i;
  2889. ELSE
  2890. ReportError("Fatal: More than one extended partition on disk");
  2891. RETURN FALSE;
  2892. END;
  2893. END;
  2894. END;
  2895. IF extStart = 0 THEN
  2896. ReportError("No extended partition found");
  2897. RETURN FALSE;
  2898. END;
  2899. IF ~GetEPBR(epbr, extStart) THEN RETURN FALSE END;
  2900. FOR i := 0 TO 15 DO slot1[i] := epbr[Slot1 + i]; slot2[i] := epbr[Slot2 + i]; END;
  2901. IF SlotEmpty(slot1) THEN (* no logical drives present *)
  2902. ASSERT(SlotEmpty(slot2));
  2903. IF ~FillinSlot(disk, partition, epbr, Slot1, type, 63, size) THEN
  2904. ReportError("Create failed: Partition too small");
  2905. RETURN FALSE;
  2906. END;
  2907. disk.device.Transfer(AosDisks.Write, extStart, 1, epbr, 0, res);
  2908. IF res # AosDisks.Ok THEN
  2909. ReportError("Could not write EPBR to logical drive partition");
  2910. RETURN FALSE;
  2911. END;
  2912. ELSE
  2913. i := extPart + 1; (* first logical drive *)
  2914. WHILE (i < LEN(disk.table)) & (AosDisks.Valid IN disk.table[i].flags) & ~(AosDisks.Primary IN disk.table[i].flags) DO INC(i); END;
  2915. (* last logical drive at disk.table[i-1] *)
  2916. lastLogical := i-1;
  2917. IF ~GetEPBR(epbr, disk.table[lastLogical].ptblock) THEN RETURN FALSE END;
  2918. FOR i := 0 TO 15 DO slot2[i] := epbr[Slot2 + i]; END;
  2919. IF ~SlotEmpty(slot2) THEN
  2920. ReportError("Could not create logical drive (slot not empty)");
  2921. RETURN FALSE;
  2922. END;
  2923. (* write new EPBR of partition to be created *)
  2924. FOR i := 0 TO BS-1 DO new[i] := 0X; END;
  2925. new[510] := 055X; new[511] := 0AAX; (* EPBR signature *)
  2926. IF ~FillinSlot(disk, partition, new, Slot1, type, 63, size) THEN
  2927. ReportError("Partition to small ");
  2928. RETURN FALSE;
  2929. END;
  2930. ASSERT(disk.table[partition].ptblock # 0); (* protects MBR *)
  2931. disk.device.Transfer(AosDisks.Write, disk.table[partition].ptblock, 1, new, 0, res);
  2932. IF res # AosDisks.Ok THEN
  2933. ReportError("Could not write EPBR to logical drive partition");
  2934. RETURN FALSE;
  2935. END;
  2936. IF ~FillinSlot(disk, partition, epbr, Slot2, TypeExt, disk.table[partition].ptblock, size) THEN
  2937. ReportError("Partition too small");
  2938. RETURN FALSE
  2939. END;
  2940. Put4(epbr, Slot2+8, disk.table[partition].ptblock - extStart); (* sector number is relative to position of EPBR of extended partition *)
  2941. disk.device.Transfer(AosDisks.Write, disk.table[lastLogical].ptblock, 1, epbr, 0, res);
  2942. IF res # AosDisks.Ok THEN
  2943. ReportError("Could not write EPBR to logical drive partition");
  2944. RETURN FALSE;
  2945. END;
  2946. END;
  2947. RETURN TRUE;
  2948. END CreateLogical;
  2949. PROCEDURE GetEPBR(VAR epbr : Block; ptblock : LONGINT) : BOOLEAN;
  2950. VAR res : WORD; result : BOOLEAN; temp: ARRAY 256 OF CHAR;
  2951. BEGIN
  2952. result := FALSE;
  2953. disk.device.Transfer(AosDisks.Read, ptblock, 1, epbr, 0, res);
  2954. IF res = AosDisks.Ok THEN
  2955. IF IsEPBR(epbr) THEN
  2956. result := TRUE;
  2957. ELSE ReportError("Delete failed: EPBR signature wrong(1)");
  2958. END;
  2959. ELSE GetErrorMsg("Delete failed: Could not load EPBR", res, temp); ReportError(temp);
  2960. END;
  2961. RETURN result;
  2962. END GetEPBR;
  2963. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  2964. BEGIN
  2965. Init^(disk, partition, out);
  2966. name := "Create"; desc := "Create partition"; locktype := WriterLock;
  2967. END Init;
  2968. END CreatePartition;
  2969. TYPE
  2970. (** Delete the specified partition. *)
  2971. DeletePartition* = OBJECT(Operation);
  2972. VAR
  2973. (* parameter: type of partition to delete *)
  2974. type : LONGINT;
  2975. PROCEDURE SetParameters*(type : LONGINT);
  2976. BEGIN
  2977. SELF.type := type;
  2978. END SetParameters;
  2979. (* dev#part type *)
  2980. PROCEDURE ValidParameters*() : BOOLEAN;
  2981. VAR valid : BOOLEAN;
  2982. BEGIN
  2983. valid := FALSE;
  2984. IF (type > 0) OR (type < 256) THEN
  2985. IF disk.device.blockSize = BS THEN
  2986. IF (disk.table # NIL) & (LEN(disk.table) > 1) & (partition < LEN(disk.table)) THEN
  2987. IF disk.table[partition].type = type THEN
  2988. IF (AosDisks.Valid IN disk.table[partition].flags) & (disk.table[partition].type # FreeSpace) THEN
  2989. valid := TRUE;
  2990. ELSE ReportError("Partition not valid");
  2991. END;
  2992. ELSE ReportError("Oldtype parameter does not match");
  2993. END;
  2994. ELSE ReportError("Device is not partitioned");
  2995. END;
  2996. ELSE ReportError("Blocksize not supported");
  2997. END;
  2998. ELSE ReportError("New type must be in [1, 255]");
  2999. END;
  3000. RETURN valid;
  3001. END ValidParameters;
  3002. PROCEDURE DoOperation*;
  3003. VAR done : BOOLEAN;
  3004. BEGIN
  3005. ASSERT((type > 0) & (type < 256));
  3006. IF disk.device.openCount = 1 THEN (* only "mounted" once, so ok to change names *)
  3007. IF (AosDisks.Primary IN disk.table[partition].flags) THEN
  3008. done := DeletePrimary(type);
  3009. ELSE (* logical drive *)
  3010. done := DeleteLogical(type);
  3011. END;
  3012. IF done THEN result.String(diskpartString); result.String(" deleted");
  3013. ELSE ReportError("Delete Failed");
  3014. END;
  3015. ELSE ReportError(" contains mounted partitions");
  3016. END;
  3017. END DoOperation;
  3018. PROCEDURE DeletePrimary(type: LONGINT) : BOOLEAN;
  3019. VAR
  3020. mbr, epbr: Block;
  3021. e, i: LONGINT; res: WORD;
  3022. result : BOOLEAN;
  3023. temp: ARRAY 256 OF CHAR;
  3024. BEGIN
  3025. result := FALSE;
  3026. ASSERT((disk.table[partition].type = type) & (disk.device.blockSize = BS) & (disk.table[partition].ptblock = 0)); (* primary partition entry is in MBR *)
  3027. disk.device.Transfer(AosDisks.Read, 0, 1, mbr, 0, res);
  3028. IF res = AosDisks.Ok THEN
  3029. IF IsMBR(mbr) THEN
  3030. e := disk.device.table[partition].ptoffset;
  3031. ASSERT(ORD(mbr[e+4]) = type); ASSERT((e >= Slot1) & (e <= Slot4)); (* entry is in partition table *)
  3032. FOR i := 0 TO 15 DO mbr[e+i] := 0X END;
  3033. disk.device.Transfer(AosDisks.Write, 0, 1, mbr, 0, res);
  3034. IF res # AosDisks.Ok THEN
  3035. GetErrorMsg("Critical: Could not store MBR, res: ", res, temp); ReportError(temp);
  3036. ELSE
  3037. result := TRUE;
  3038. IF IsExtendedPartition(type) THEN
  3039. (* delete EPBR on extended partition *)
  3040. IF GetEPBR(epbr, disk.table[partition].start) THEN
  3041. FOR i := 0 TO BS-1 DO epbr[i] := 0X; END;
  3042. ASSERT(disk.table[partition].start#0);
  3043. disk.device.Transfer(AosDisks.Write, disk.table[partition].start, 1, epbr, 0, res);
  3044. IF res # AosDisks.Ok THEN
  3045. ReportError("Could not delete EPBR signature of extended partition");
  3046. result := FALSE;
  3047. END;
  3048. ELSE
  3049. ReportError("Could not delete EPBR signature of extended partition (EPBR not found)");
  3050. result := FALSE;
  3051. END;
  3052. END;
  3053. END;
  3054. ELSE ReportError("Delete failed: MBR signature wrong");
  3055. END;
  3056. ELSE GetErrorMsg("Delete failed: Could not load MBR, res: ", res, temp); ReportError(temp);
  3057. END;
  3058. RETURN result;
  3059. END DeletePrimary;
  3060. PROCEDURE DeleteLogical(type: LONGINT) : BOOLEAN;
  3061. VAR
  3062. epbr, temp : Block;
  3063. nextLogical, i, start : LONGINT; res: WORD;
  3064. slot2 : ARRAY 16 OF CHAR;
  3065. extStart : LONGINT; (* adr of EPBR of extended partition *)
  3066. writebackAdr : LONGINT;
  3067. BEGIN
  3068. ASSERT((disk.table[partition].type = type) & (disk.device.blockSize = BS) & (disk.table[partition].ptblock # 0)); (* logical partition entry not in MBR *)
  3069. (* Extended partitions work the following way:
  3070. - There's at most one extended partition entry in the MBR
  3071. - The first sector of an extended partition contains the Extended Partition Boot Record (EPBR)
  3072. - The structure of the EPBR is similar to the MBR's structure, but...
  3073. - only partition table & signature (no executable code)
  3074. - slots 2&3 are always zero
  3075. - the first slot describes a logical drive
  3076. - the second slot points to the next EBPR (~ next logical drive)
  3077. - the address of the next EPBR is : ExtPartition.start + <second slot number of sectors between MBR & first sector field>
  3078. -> linked list
  3079. *)
  3080. (* get start block of extended partition *)
  3081. extStart := 0;
  3082. FOR i := 0 TO LEN(disk.device.table)-1 DO
  3083. IF IsExtendedPartition(disk.device.table[i].type) THEN
  3084. IF extStart = 0 THEN
  3085. extStart := disk.device.table[i].start;
  3086. ELSE
  3087. ReportError("Fatal: More than one extended partition on disk");
  3088. RETURN FALSE;
  3089. END;
  3090. END;
  3091. END;
  3092. IF extStart = 0 THEN
  3093. ReportError("No extended partition found");
  3094. RETURN FALSE;
  3095. END;
  3096. (* get the "pointer" to the next EPBR; we take the whole slot 2*)
  3097. IF ~GetEPBR(epbr, disk.table[partition].ptblock) THEN RETURN FALSE END;
  3098. FOR i := 0 TO 15 DO slot2[i] := epbr[Slot2 + i]; END;
  3099. (* now get the epbr which contains the "pointer" to the partition we want to delete *)
  3100. IF disk.table[partition].ptblock = extStart THEN (* entry is in EPBR of extended partition *)
  3101. writebackAdr := extStart;
  3102. IF ~GetEPBR(epbr, extStart) THEN RETURN FALSE END; (* EPBR of extended partition *)
  3103. IF SlotEmpty(slot2) THEN (* only one logical drive; just delete slot 1 ;-) *)
  3104. FOR i := 0 TO 15 DO epbr[Slot1 + i] := 0X END;
  3105. ELSE (* need to replace slot1&2 *)
  3106. (* first we get slot1 & 2 of the next logical drive *)
  3107. nextLogical := extStart + Get4(slot2, 8);
  3108. IF ~GetEPBR(temp, nextLogical) THEN RETURN FALSE END;
  3109. start := Get4(temp, Slot1 + 8) + Get4(slot2, 8);
  3110. Put4(temp, Slot1 + 8, start);
  3111. FOR i := 0 TO 15 DO
  3112. epbr[Slot1 + i] := temp[Slot1 + i];
  3113. epbr[Slot2 + i] := temp[Slot2 +i];
  3114. END;
  3115. END;
  3116. ELSE
  3117. (* we need the logical drive whos EBPR "points to" the logical drive we want to delete...*)
  3118. IF (partition-1 > 0) & ~(AosDisks.Primary IN disk.table[partition-1].flags) THEN
  3119. writebackAdr := disk.table[partition-1].ptblock;
  3120. IF ~ GetEPBR(epbr, disk.table[partition-1].ptblock) THEN RETURN FALSE END;
  3121. FOR i := 0 TO 15 DO epbr[Slot2+i] := slot2[i]; END;
  3122. ELSE
  3123. ReportError("Can't find EPBR of previous logical drive");
  3124. RETURN FALSE;
  3125. END;
  3126. END;
  3127. (* write back EPBR of extended partition *)
  3128. ASSERT(writebackAdr#0);
  3129. disk.device.Transfer(AosDisks.Write, writebackAdr, 1, epbr, 0, res);
  3130. IF res # AosDisks.Ok THEN
  3131. GetErrorMsg("Critical: Could not store EPBR of extended partition", res, temp); ReportError(temp);
  3132. RETURN FALSE;
  3133. END;
  3134. RETURN TRUE;
  3135. END DeleteLogical;
  3136. PROCEDURE GetEPBR(VAR epbr : Block; ptblock : LONGINT) : BOOLEAN;
  3137. VAR res : WORD; result : BOOLEAN; temp: ARRAY 256 OF CHAR;
  3138. BEGIN
  3139. result := FALSE;
  3140. disk.device.Transfer(AosDisks.Read, ptblock, 1, epbr, 0, res);
  3141. IF res = AosDisks.Ok THEN
  3142. IF IsEPBR(epbr) THEN
  3143. result := TRUE;
  3144. ELSE ReportError("Delete failed: EPBR signature wrong(1)");
  3145. END;
  3146. ELSE GetErrorMsg("Delete failed: Could not load EPBR", res, temp); ReportError(temp);
  3147. END;
  3148. RETURN result;
  3149. END GetEPBR;
  3150. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  3151. BEGIN
  3152. Init^(disk, partition, out);
  3153. name := "Delete"; desc := "Delete partition"; locktype := WriterLock;
  3154. END Init;
  3155. END DeletePartition;
  3156. TYPE
  3157. (** Set or clear the active bit of the specified partition. *)
  3158. SetFlags* = OBJECT(Operation);
  3159. VAR
  3160. (* Parameters *)
  3161. on : BOOLEAN;
  3162. PROCEDURE SetParameters*(on : BOOLEAN);
  3163. BEGIN
  3164. SELF.on := on;
  3165. END SetParameters;
  3166. PROCEDURE ValidParameters*() : BOOLEAN;
  3167. VAR valid : BOOLEAN;
  3168. BEGIN
  3169. valid := FALSE;
  3170. IF disk.device.blockSize = BS THEN
  3171. IF ~disk.isDiskette THEN
  3172. valid := TRUE;
  3173. ELSE ReportError("Operation not supported for floppy disk drives");
  3174. END;
  3175. ELSE ReportError("Blocksize not supported");
  3176. END;
  3177. RETURN valid;
  3178. END ValidParameters;
  3179. (* Set active bit of dev#part to <on> *)
  3180. PROCEDURE DoOperation*;
  3181. VAR
  3182. res: WORD; e: LONGINT; b: ARRAY BS OF CHAR; mod: BOOLEAN;
  3183. string : ARRAY 256 OF CHAR;
  3184. BEGIN
  3185. disk.device.Transfer(AosDisks.Read, disk.device.table[partition].ptblock, 1, b, 0, res);
  3186. IF res = AosDisks.Ok THEN
  3187. ASSERT((b[510] = 055X) & (b[511] = 0AAX));
  3188. e := disk.device.table[partition].ptoffset;
  3189. IF (e >= Slot1) & (e <= Slot4) THEN
  3190. mod := FALSE;
  3191. IF on & (b[e] = 0X) THEN b[e] := 80X; mod := TRUE
  3192. ELSIF ~on & ((b[e] >= 80X) & (b[e] <= 81X)) THEN b[e] := 0X; mod := TRUE
  3193. END;
  3194. IF mod THEN
  3195. disk.device.Transfer(AosDisks.Write, disk.device.table[partition].ptblock, 1, b, 0, res);
  3196. IF res = AosDisks.Ok THEN
  3197. IF on THEN
  3198. INCL(disk.device.table[partition].flags, AosDisks.Boot);
  3199. INCL(disk.table[partition].flags, AosDisks.Boot);
  3200. result.String(diskpartString); result.String(" activated");
  3201. ELSE
  3202. EXCL(disk.device.table[partition].flags, AosDisks.Boot);
  3203. EXCL(disk.table[partition].flags, AosDisks.Boot);
  3204. result.String(diskpartString); result.String(" deactivated");
  3205. END
  3206. ELSE GetTransferError(disk.device, AosDisks.Write, disk.device.table[partition].ptblock, res, string); ReportError(string);
  3207. END
  3208. ELSE
  3209. string := ""; Strings.Append(string, diskpartString);
  3210. IF on THEN Strings.Append(string, " already active"); ELSE Strings.Append(string, " already inactive"); END;
  3211. ReportError(string);
  3212. END
  3213. ELSE ReportError("not a valid partition");
  3214. END
  3215. ELSE GetTransferError(disk.device, AosDisks.Write, disk.device.table[partition].ptblock, res, string); ReportError(string);
  3216. END;
  3217. END DoOperation;
  3218. PROCEDURE &Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  3219. BEGIN
  3220. Init^(disk, partition, out);
  3221. name := "(In)Activate"; desc := "Set/clear active bit of partition"; locktype := WriterLock;
  3222. END Init;
  3223. END SetFlags;
  3224. TYPE
  3225. (** *)
  3226. (* Install the Bluebottle boot manager on the specified partition *)
  3227. (* Example of a compound operation. *)
  3228. InstallBootManager* = OBJECT(Operation)
  3229. VAR
  3230. mbrFilename, restFilename : ARRAY 1024 OF CHAR; (* boot manager *)
  3231. PROCEDURE SetParameters*(CONST mbrFilename, restFilename : ARRAY OF CHAR);
  3232. BEGIN
  3233. SELF.mbrFilename := ""; Strings.Append(SELF.mbrFilename, mbrFilename);
  3234. SELF.restFilename := ""; Strings.Append(SELF.restFilename, restFilename);
  3235. END SetParameters;
  3236. PROCEDURE ValidParameters*() : BOOLEAN;
  3237. BEGIN
  3238. IF disk.device.blockSize # BS THEN
  3239. ReportError("InstallBootManager only works with 512B block size"); RETURN FALSE;
  3240. END;
  3241. IF partition # 0 THEN
  3242. ReportError("The only valid selection is partition 0"); RETURN FALSE;
  3243. END;
  3244. RETURN TRUE;
  3245. END ValidParameters;
  3246. PROCEDURE DoOperation*;
  3247. VAR
  3248. writeMBR : WriteMBR; fileToPartition : FileToPartition;
  3249. nofSectors : LONGINT;
  3250. string : ARRAY 1024 OF CHAR;
  3251. f : Files.File;
  3252. BEGIN
  3253. (* Before we write data to the MBR, first check whether the rest of the boot manager is present *)
  3254. f := Files.Old(restFilename);
  3255. IF f = NIL THEN
  3256. string := "File "; Strings.Append(string, restFilename); Strings.Append(string, " not found.");
  3257. ReportError(string);
  3258. RETURN;
  3259. END;
  3260. IF (f.Length() MOD BS # 0) THEN
  3261. ReportError("Boot manager file size must be multiple of 512B");
  3262. RETURN;
  3263. END;
  3264. nofSectors := f.Length() DIV BS;
  3265. IF (nofSectors > 20) THEN
  3266. ReportError("Boot manager file is too large. Wrong file?");
  3267. RETURN;
  3268. END;
  3269. (* Do the actual operation *)
  3270. NEW(writeMBR, disk, partition, out);
  3271. writeMBR.SetParent(SELF);
  3272. writeMBR.SetParameters(mbrFilename, TRUE, TRUE);
  3273. writeMBR.SetBlockingStart;
  3274. IF writeMBR.state.errorCount = 0 THEN
  3275. NEW(fileToPartition, disk, partition, out);
  3276. fileToPartition.SetParent(SELF);
  3277. fileToPartition.SetParameters(restFilename, 1, nofSectors);
  3278. fileToPartition.SetBlockingStart;
  3279. IF fileToPartition.state.errorCount = 0 THEN
  3280. result.String("Boot manager has been written to "); result.String(diskpartString);
  3281. ELSE
  3282. ReportError("FileToPartition operation failed.");
  3283. END;
  3284. ELSE
  3285. ReportError("WriteMBR operation failed.");
  3286. END;
  3287. END DoOperation;
  3288. PROCEDURE & Init*(disk : Disk; partition : LONGINT; out : Streams.Writer);
  3289. BEGIN
  3290. Init^(disk, partition, out);
  3291. name := "InstallBootManager"; desc := "Install Boot Manager on partition"; locktype := WriterLock;
  3292. END Init;
  3293. END InstallBootManager;
  3294. VAR
  3295. safe*: BOOLEAN;
  3296. diskModel- : DisksModel;
  3297. operations- : OperationManager;
  3298. infobus- : CompletionNotification;
  3299. BootLoaderName: ARRAY 64 OF CHAR;
  3300. BootLoaderSize: LONGINT;
  3301. PROCEDURE WritePart*(w: Streams.Writer; dev: AosDisks.Device; part: LONGINT);
  3302. BEGIN
  3303. ASSERT((dev#NIL) & (w # NIL) & (part >= 0) & (part <= 99));
  3304. w.String(dev.name); w.String("#"); w.Int(part,1); w.String(" ");
  3305. END WritePart;
  3306. (* Check if an Oberon file system is present on a partition. Returns 0 if no Oberon file system found,
  3307. 1 for a Native file system, 2 for an old Aos file system and 3 for a new Aos file system, 4 for unknown but
  3308. valid boot signature *)
  3309. PROCEDURE DetectFS*(dev: AosDisks.Device; part: LONGINT): LONGINT;
  3310. VAR b: ARRAY BS OF CHAR; res: WORD; fs: LONGINT; doClose : BOOLEAN;
  3311. BEGIN
  3312. IF dev.blockSize # BS THEN RETURN 0; END;
  3313. (* special handling for diskettes *)
  3314. IF Strings.Match("Diskette*", dev.name) & (dev.openCount < 1) THEN
  3315. doClose := TRUE;
  3316. dev.Open(res);
  3317. IF res # AosDisks.Ok THEN RETURN 0 END;
  3318. END;
  3319. IF (dev.table = NIL) OR (part >= LEN(dev.table)) THEN RETURN 0 END;
  3320. fs := UnknownFS;
  3321. dev.Transfer(AosDisks.Read, dev.table[part].start, 1, b, 0, res);
  3322. IF res = AosDisks.Ok THEN
  3323. IF (b[1FEH] = 055X) & (b[1FFH] = 0AAX) THEN
  3324. b[0] := "x"; b[1] := "x"; b[2] := "x"; b[9] := 0X;
  3325. IF Get4(b, 1F8H) = FSID THEN
  3326. IF (b[1FCH] = CHR(1)) THEN
  3327. fs := AosFS32;
  3328. ELSIF (b[1FCH] = CHR(2)) THEN
  3329. fs := AosFS128;
  3330. ELSE
  3331. fs := UnknownFS;
  3332. END;
  3333. ELSIF Get4(b, 1F8H) = FSID0 THEN fs := OldAosFS32;
  3334. ELSIF b = "xxxOBERON" THEN fs := NativeFS;
  3335. ELSE fs := FatFS;
  3336. END
  3337. ELSE (* skip *)
  3338. END
  3339. END;
  3340. IF doClose & (dev.openCount > 0) THEN (* it's a diskette *)
  3341. dev.Close(res); (* ignore res *)
  3342. END;
  3343. RETURN fs
  3344. END DetectFS;
  3345. (** Performs a read on the specified device to see whether a medium is present *)
  3346. PROCEDURE DisketteInserted*(dev : AosDisks.Device) : BOOLEAN;
  3347. VAR res : WORD; b : ARRAY BS OF CHAR;
  3348. BEGIN
  3349. IF (dev = NIL) OR (dev.blockSize # BS) THEN RETURN FALSE END;
  3350. dev.Transfer(AosDisks.Read, 0, 1, b, 0, res);
  3351. RETURN res = AosDisks.Ok;
  3352. END DisketteInserted;
  3353. (* Read OBL variables from the specified partition. *)
  3354. PROCEDURE GetVars(dev: AosDisks.Device; part: LONGINT; VAR tsize, reserved, fsOfs: LONGINT; VAR res: WORD);
  3355. VAR b: ARRAY BS OF CHAR;
  3356. BEGIN
  3357. ASSERT(dev.blockSize = BS);
  3358. dev.Transfer(AosDisks.Read, dev.table[part].start, 1, b, 0, res);
  3359. IF res = AosDisks.Ok THEN
  3360. b[0] := "x"; b[1] := "x"; b[2] := "x"; b[9] := 0X;
  3361. ASSERT(b = "xxxOBERON"); (* OBL present *)
  3362. tsize := ORD(b[10H]); ASSERT(tsize > 0);
  3363. reserved := Get2(b, 0EH); ASSERT(reserved >= BootLoaderSize + tsize);
  3364. IF Get4(b, 1F8H) = FSID THEN fsOfs := Get4(b, 1F0H) ELSE fsOfs := reserved END
  3365. END
  3366. END GetVars;
  3367. (* Write the specified file to the device, starting at block pos. *)
  3368. PROCEDURE WriteFile(f: Files.File; dev: AosDisks.Device; pos: LONGINT; VAR sum: LONGINT; VAR res: WORD);
  3369. CONST Size = 32;
  3370. VAR buf: ARRAY Size*BS OF CHAR; r: Files.Rider; n, num: LONGINT;
  3371. BEGIN
  3372. ASSERT(dev.blockSize = BS);
  3373. f.Set(r, 0); num := (f.Length()+BS-1) DIV BS; sum := 0;
  3374. LOOP
  3375. IF num <= 0 THEN EXIT END;
  3376. f.ReadBytes(r, buf, 0, Size*BS);
  3377. n := Size*BS - r.res;
  3378. WHILE n MOD BS # 0 DO buf[n] := 0X; INC(n) END;
  3379. ASSERT((n > 0) & (n <= num*BS));
  3380. dev.Transfer(AosDisks.Write, pos, n DIV BS, buf, 0, res);
  3381. IF res # AosDisks.Ok THEN EXIT END;
  3382. DEC(num, n DIV BS); INC(pos, n DIV BS);
  3383. REPEAT DEC(n); sum := (sum + ORD(buf[n])) MOD 100H UNTIL n = 0
  3384. END;
  3385. sum := (-sum) MOD 100H
  3386. END WriteFile;
  3387. PROCEDURE CheckFile(f: Files.File; dev: AosDisks.Device; pos: LONGINT; sum: LONGINT; VAR res: WORD);
  3388. CONST Size = 32;
  3389. VAR buf1, buf2: ARRAY Size*BS OF CHAR; r: Files.Rider; n, num, i: LONGINT;
  3390. BEGIN
  3391. ASSERT(dev.blockSize = BS);
  3392. f.Set(r, 0); num := (f.Length()+BS-1) DIV BS;
  3393. LOOP
  3394. IF num <= 0 THEN EXIT END;
  3395. f.ReadBytes(r, buf1, 0, Size*BS);
  3396. n := Size*BS - r.res;
  3397. WHILE n MOD BS # 0 DO buf1[n] := 0X; INC(n) END;
  3398. ASSERT((n > 0) & (n <= num*BS));
  3399. dev.Transfer(AosDisks.Read, pos, n DIV BS, buf2, 0, res);
  3400. IF res # AosDisks.Ok THEN EXIT END;
  3401. i := 0;
  3402. WHILE i # n DO
  3403. IF buf1[i] # buf2[i] THEN res := CoreMismatch; EXIT END;
  3404. INC(i)
  3405. END;
  3406. DEC(num, n DIV BS); INC(pos, n DIV BS);
  3407. REPEAT DEC(n); sum := (sum + ORD(buf2[n])) MOD 100H UNTIL n = 0
  3408. END;
  3409. IF (res = AosDisks.Ok) & (sum # 0) THEN res := CoreChecksumError END;
  3410. END CheckFile;
  3411. (* Write a boot file on the specified partition. *)
  3412. PROCEDURE InitBootFile(dev: AosDisks.Device; part: LONGINT; f: Files.File; VAR res: WORD);
  3413. CONST Frag = 7; LoadAdr = 1000H; StartAdr = 1000H; Frags = 1;
  3414. VAR config: Configuration; i, tsize, reserved, fsOfs, sum, start: LONGINT; data: ARRAY 12+8*Frags OF CHAR;
  3415. BEGIN
  3416. NEW(config);
  3417. config.GetTable(dev, part, res);
  3418. IF res = AosDisks.Ok THEN
  3419. LOOP
  3420. i := config.FindEntry(0, Frag);
  3421. IF i < 0 THEN EXIT END;
  3422. config.DeleteEntry(i)
  3423. END;
  3424. GetVars(dev, part, tsize, reserved, fsOfs, res);
  3425. IF res = AosDisks.Ok THEN
  3426. start := BootLoaderSize+tsize;
  3427. IF (fsOfs-start)*BS >= f.Length() THEN
  3428. WriteFile(f, dev, dev.table[part].start + start, sum, res);
  3429. IF res = AosDisks.Ok THEN CheckFile(f, dev, dev.table[part].start + start, sum, res) END;
  3430. IF res = AosDisks.Ok THEN
  3431. Put4(data, 0, LoadAdr); Put4(data, 4, Frags + ASH(sum, 16));
  3432. Put4(data, 8, StartAdr); Put4(data, 12, 0); (* pos relative to start *)
  3433. Put4(data, 16, (f.Length()+BS-1) DIV BS);
  3434. config.AddEntry(Frag, LEN(data), data);
  3435. config.PutTable(dev, part, res)
  3436. END
  3437. ELSE
  3438. res := NoSpaceAvailable (* not enough space available for boot file *)
  3439. END
  3440. END
  3441. END
  3442. END InitBootFile;
  3443. PROCEDURE Eject*(dev : AosDisks.Device; VAR result: ARRAY OF CHAR);
  3444. VAR msg: AosDisks.EjectMsg; res : WORD; temp: ARRAY 256 OF CHAR;
  3445. BEGIN
  3446. ASSERT(dev#NIL);
  3447. COPY (dev.name, result);
  3448. dev.Handle(msg, res);
  3449. IF res = AosDisks.Ok THEN
  3450. Strings.Append(result, " ejected");
  3451. ELSE
  3452. GetErrorMsg(" ejection failed: ", res, temp); Strings.Append(result, temp);
  3453. END;
  3454. END Eject;
  3455. PROCEDURE Sync*(dev: AosDisks.Device; VAR result: ARRAY OF CHAR);
  3456. VAR msg: AosDisks.SyncMsg; res: WORD; temp: ARRAY 256 OF CHAR;
  3457. BEGIN
  3458. ASSERT(dev # NIL);
  3459. COPY(dev.name, result);
  3460. dev.Handle(msg, res);
  3461. IF res = AosDisks.Ok THEN
  3462. Strings.Append(result, " synchronized");
  3463. ELSE
  3464. GetErrorMsg(" synchronization failed: ", res, temp); Strings.Append(result, temp);
  3465. END;
  3466. END Sync;
  3467. PROCEDURE ShowAosFSLimits*;
  3468. CONST Unit = 1024*1024*1024;
  3469. VAR string : ARRAY 32 OF CHAR;
  3470. BEGIN
  3471. KernelLog.String("* Aos file system limits with "); KernelLog.Int(AosSS, 0); KernelLog.String(" byte sectors:"); KernelLog.Ln;
  3472. Strings.FloatToStr( 1.0D0*MAX(LONGINT)/Unit, 1, 2, 0, string);
  3473. KernelLog.String(" "); KernelLog.String(string); KernelLog.String(" Gb positioning limit in file because of 31 bit Set & Pos parameters"); KernelLog.Ln;
  3474. Strings.FloatToStr(((1.0D0*AosXS*AosXS+AosSTS)*AosSS-AosHS)/Unit, 1, 2, 0, string);
  3475. KernelLog.String(" "); KernelLog.String(string); KernelLog.String(" Gb file size limit because of triple index structure"); KernelLog.Ln;
  3476. Strings.FloatToStr(1.0D0*MAX(LONGINT)/AosSF*AosSS/Unit, 1, 2, 0, string);
  3477. KernelLog.String(" "); KernelLog.String(string); KernelLog.String(" Gb volume size limit because of sector factor"); KernelLog.Ln;
  3478. Strings.FloatToStr((1.0D0*MAX(LONGINT)+1)*AosSS/Unit, 1, 2, 0, string);
  3479. KernelLog.String(" "); KernelLog.String(string); KernelLog.String(" Gb file size limit because of 31 bit apos field"); KernelLog.Ln;
  3480. END ShowAosFSLimits;
  3481. (* Helper procedures *)
  3482. PROCEDURE IsMBR(CONST mbr : Block) : BOOLEAN;
  3483. BEGIN
  3484. RETURN ((mbr[510] = 55X) & (mbr[511] = 0AAX));
  3485. END IsMBR;
  3486. PROCEDURE IsEPBR(CONST epbr : Block) : BOOLEAN;
  3487. VAR i : LONGINT; result : BOOLEAN;
  3488. BEGIN
  3489. result := ((epbr[510] = 055X) & (epbr[511] = 0AAX)); (* EPBR signature *)
  3490. FOR i := 1DEH TO 1DEH+31 DO (* last two slots should be zero *)
  3491. IF epbr[i]#0X THEN result := FALSE; END;
  3492. END;
  3493. RETURN result;
  3494. END IsEPBR;
  3495. PROCEDURE SlotEmpty(CONST slot : ARRAY OF CHAR) : BOOLEAN;
  3496. VAR result : BOOLEAN; i : LONGINT;
  3497. BEGIN
  3498. result := TRUE;
  3499. IF LEN(slot)#16 THEN RETURN FALSE END;
  3500. FOR i := 0 TO 15 DO
  3501. IF slot[i]#0X THEN result := FALSE END;
  3502. END;
  3503. RETURN result;
  3504. END SlotEmpty;
  3505. PROCEDURE FillinSlot(disk : Disk; partition : LONGINT; VAR bootrecord : Block; slot, type, start, size : LONGINT) : BOOLEAN;
  3506. VAR spt, hds, end, t : LONGINT;
  3507. BEGIN
  3508. ASSERT((slot = Slot1) OR (slot = Slot2) OR (slot = Slot3) OR (slot = Slot4)); ASSERT(disk.gres=AosDisks.Ok);
  3509. spt := disk.geo.spt; hds := disk.geo.hds;
  3510. INC(size, (-(start+size)) MOD (hds*spt)); (* round end up to cylinder boundary *)
  3511. IF size > disk.table[partition].size THEN size := disk.table[partition].size END; (* clip size down to max *)
  3512. IF size >= MinPartSize THEN (* create the entry *)
  3513. end := start + size - 1;
  3514. bootrecord[slot] := 0X; (* not bootable *)
  3515. bootrecord[slot+1] := CHR((start DIV spt) MOD hds);
  3516. t := start DIV (spt*hds);
  3517. IF t > 1023 THEN t := 1023 END;
  3518. bootrecord[slot+2] := CHR(ASH(ASH(t, -8), 6) + (start MOD spt) + 1);
  3519. bootrecord[slot+3] := CHR(t MOD 256);
  3520. bootrecord[slot+4] := CHR(type);
  3521. bootrecord[slot+5] := CHR((end DIV spt) MOD hds);
  3522. t := end DIV (spt*hds);
  3523. IF t > 1023 THEN t := 1023 END;
  3524. bootrecord[slot+6] := CHR(ASH(ASH(t, -8), 6) + (end MOD spt) + 1);
  3525. bootrecord[slot+7] := CHR(t MOD 256);
  3526. Put4(bootrecord, slot+8, start);
  3527. Put4(bootrecord, slot+12, size);
  3528. RETURN TRUE;
  3529. ELSE
  3530. RETURN FALSE;
  3531. END;
  3532. END FillinSlot;
  3533. (* Returns TRUE if partition type is extended partition type *)
  3534. PROCEDURE IsExtendedPartition(type: LONGINT): BOOLEAN;
  3535. BEGIN
  3536. RETURN (type = 5) OR (type = 15);
  3537. END IsExtendedPartition;
  3538. PROCEDURE IsNativeType*(type: LONGINT) : BOOLEAN;
  3539. BEGIN
  3540. RETURN (type = NativeType1) OR (type = NativeType2) OR (type = AosType)
  3541. END IsNativeType;
  3542. PROCEDURE IsFatType*(type : LONGINT) : BOOLEAN;
  3543. BEGIN
  3544. RETURN (type = 4) OR (type = 6) OR (type = 0EH) OR (type = 1) OR (type = 0BH) OR (type = 0CH);
  3545. END IsFatType;
  3546. PROCEDURE IsPartitioned(dev : AosDisks.Device) : BOOLEAN;
  3547. BEGIN
  3548. RETURN (dev # NIL) & (dev.table # NIL) & (dev.table[0].flags * {AosDisks.Valid} # {});
  3549. END IsPartitioned;
  3550. (* Decide heuristically which BIOS drive number to use when booting from the specified device. *)
  3551. PROCEDURE GetDriveNum*(dev: AosDisks.Device): CHAR;
  3552. VAR d: CHAR;
  3553. BEGIN
  3554. (* for removable media, assume the BIOS drive number is 0H, otherwise 80H. *)
  3555. (* The caller has opened the device, so IsPartitioned can access the partition table. *)
  3556. IF ~IsPartitioned(dev) & (AosDisks.Removable IN dev.flags) THEN d := 0X ELSE d := 80X END;
  3557. RETURN d
  3558. END GetDriveNum;
  3559. PROCEDURE Put2*(VAR b: ARRAY OF CHAR; i, val: LONGINT);
  3560. BEGIN
  3561. ASSERT((val >= 0) & (val < 10000H));
  3562. b[i] := CHR(val MOD 100H);
  3563. b[i+1] := CHR(ASH(val, -8) MOD 100H);
  3564. END Put2;
  3565. PROCEDURE Put4*(VAR b: ARRAY OF CHAR; i, val: LONGINT);
  3566. BEGIN
  3567. b[i] := CHR(val MOD 100H);
  3568. b[i+1] := CHR(ASH(val, -8) MOD 100H);
  3569. b[i+2] := CHR(ASH(val, -16) MOD 100H);
  3570. b[i+3] := CHR(ASH(val, -24) MOD 100H);
  3571. END Put4;
  3572. PROCEDURE Get2*(CONST b: ARRAY OF CHAR; i: LONGINT): LONGINT;
  3573. BEGIN
  3574. RETURN ORD(b[i]) + ASH(ORD(b[i+1]), 8);
  3575. END Get2;
  3576. PROCEDURE Get4*(CONST b: ARRAY OF CHAR; i: LONGINT): LONGINT;
  3577. BEGIN
  3578. RETURN ORD(b[i]) + ASH(ORD(b[i+1]), 8) + ASH(ORD(b[i+2]), 16) + ASH(ORD(b[i+3]), 24);
  3579. END Get4;
  3580. (* Write partition type *)
  3581. PROCEDURE WriteType*(type: LONGINT; VAR s : ARRAY OF CHAR; VAR color : WMGraphics.Color);
  3582. CONST
  3583. ColorFAT12 = WMGraphics.Red;
  3584. ColorFAT16 = WMGraphics.Red;
  3585. ColorFAT32 = WMGraphics.Red;
  3586. ColorOberon = WMGraphics.Blue;
  3587. ColorDefault = WMGraphics.Black;
  3588. ColorExtended = WMGraphics.White;
  3589. BEGIN
  3590. (* list from Linux fdisk, Microsoft Partitioning Summary (Q69912), Hal Landis' list & Jacques Eloff, http://home.global.co.za/~eloffjl/parcodes.html *)
  3591. color := ColorDefault;
  3592. CASE type OF
  3593. |001H: s := "DOS FAT12"; color := ColorFAT12;
  3594. |002H: s := "Xenix root"
  3595. |003H: s := "Xenix usr"
  3596. |004H: s := "DOS FAT16 < 32M"; color := ColorFAT16;
  3597. |005H: s := "Extended"; color := ColorExtended;
  3598. |006H: s := "DOS FAT16 >= 32M"; color := ColorFAT16;
  3599. |007H: s := "NTFS, HPFS, QNX, Adv. Unix"
  3600. |008H: s := "AIX boot, SplitDrive, QNX qny"
  3601. |009H: s := "AIX data, Coherent swap, QNX qnz"
  3602. |00AH: s := "OS/2 BM, Coherent swap"
  3603. |00BH: s := "Win 95/98, FAT32"; color := ColorFAT32;
  3604. |00CH: s := "Win 95/98, FAT32 LBA"; color := ColorFAT32;
  3605. |00EH: s := "DOS FAT16 LBA"; color := ColorFAT16;
  3606. |00FH: s := "Extended LBA"; color := ColorExtended;
  3607. |010H: s := "Opus"
  3608. |011H: s := "OS/2 BM: Hidden FAT12"
  3609. |012H: s := "Xenix, SCO, Compaq diag."
  3610. |013H: s := "Xenix, SCO"
  3611. |014H: s := "OS/2 BM: Hidden FAT16 < 32M"
  3612. |016H: s := "OS/2 BM: Hidden FAT16 >= 32M"
  3613. |017H: s := "OS/2 BM: Hidden IFS"
  3614. |018H: s := "AST Windows"
  3615. |019H: s := "Interactive Unix, SCO"
  3616. |024H: s := "NEC DOS"
  3617. |028H..029H: s := "THEOS"
  3618. |038H..039H: s := "THEOS"
  3619. |03CH: s := "PQMagic recovery"
  3620. |040H: s := "Venix 80286"
  3621. |041H: s := "Linux/Minix, DR-DOS"
  3622. |042H: s := "SFS, Linux swap, DR-DOS"
  3623. |043H: s := "Linux fs, DR-DOS"
  3624. |04CH: s := "Native Oberon, Aos"; color := ColorOberon;
  3625. |04DH: s := "Switcherland or QNX Posix"
  3626. |04EH: s := "Active or QNX Posix"
  3627. |04FH: s := "Native Oberon or QNX Posix"
  3628. |050H: s := "Native Oberon alt. or Lynx RTOS, DM"
  3629. |051H: s := "Novell Netware, Ontrack Ext, DM6 Aux 1"
  3630. |052H: s := "Microport SysV/AT, CP/M"
  3631. |053H: s := "DM6 Aux 3"
  3632. |054H: s := "NTFS, DM6"
  3633. |055H: s := "EZ-Drive, DM"
  3634. |056H: s := "Golden Bow, DM"
  3635. |05CH: s := "Priam EDisk, DM"
  3636. |05DH..05EH: s := "QNX"
  3637. |061H: s := "SpeedStor"
  3638. |062H: s := "Pick"
  3639. |063H: s := "GNU HURD, Mach, Sys V/386, ISC UNIX"
  3640. |064H: s := "Novell Netware 286"
  3641. |065H: s := "Novell Netware 386"
  3642. |066H..69H: s := "Novell Netware"
  3643. |070H: s := "Disk Secure Multi-Boot"
  3644. |072H: s := "Pick"
  3645. |073H: s := "Unix, SCO"
  3646. |074H: s := "Novell Netware"
  3647. |075H: s := "PC/IX"
  3648. |077H..079H: s := "QNX 4.x"
  3649. |080H: s := "Minix <= 1.4a"
  3650. |081H: s := "Minix > 1.4b, old Linux, Mitax DM"
  3651. |082H: s := "Linux swap"
  3652. |083H: s := "Linux fs"
  3653. |084H: s := "OS/2 Hidden C: drive"
  3654. |085H: s := "Linux ext"
  3655. |086H..087H: s := "NTFS volume"
  3656. |093H..094H: s := "Amoeba"
  3657. |0A0H: s := "IBM Thinkpad hibernation"
  3658. |0A5H: s := "BSD i386"
  3659. |0A7H: s := "NeXTSTEP 486"
  3660. |0B5H: s := "FreeBSD"
  3661. |0B7H: s := "BSDI fs"
  3662. |0B8H: s := "BSDI swap"
  3663. |0C0H: s := "CTOS"
  3664. |0C1H: s := "DRDOS/sec FAT12"
  3665. |0C4H: s := "DRDOS/sec FAT16 < 32M"
  3666. |0C6H: s := "DRDOS/sec FAT16 >= 32M"
  3667. |0C7H: s := "Syrinx"
  3668. |0CBH: s := "CP/M, DR"
  3669. |0CDH: s := "CTOS, Mem"
  3670. |0D0H: s := "CTOS"
  3671. |0DBH: s := "CP/M, Concurrent CP/M, DOS, CTOS"
  3672. |0DDH: s := "CTOS, Mem"
  3673. |0DFH: s := "Datafusion"
  3674. |0E1H: s := "DOS access, SpeedStor FAT12 ext"
  3675. |0E2H: s := "Gneiss"
  3676. |0E3H: s := "DOS R/O, SpeedStor, Oberon old"
  3677. |0E4H: s := "SpeedStor FAT16 ext"
  3678. |0F1H: s := "SpeedStor"
  3679. |0F2H: s := "DOS 3.3 secondary"
  3680. |0F4H: s := "SpeedStor large"
  3681. |0FEH: s := "SpeedStor > 1024 cyl, LANstep"
  3682. |0FFH: s := "Xenix BBT"
  3683. |WholeDisk: s := "Whole disk"; color := WMGraphics.RGBAToColor(200,200,200,255);
  3684. |-1: s := "Unallocated"; color := WMGraphics.RGBAToColor(200,200,200,255);
  3685. |-2: s := "Reserved" (* boot records, alignment, test track *)
  3686. ELSE s := "Unknown"
  3687. END;
  3688. END WriteType;
  3689. PROCEDURE GetErrorMsg*(CONST msg: ARRAY OF CHAR; res: WORD; VAR string: ARRAY OF CHAR);
  3690. VAR temp : ARRAY 32 OF CHAR;
  3691. BEGIN
  3692. IF res = AosDisks.MediaChanged THEN string := " (res: media changed)";
  3693. ELSIF res = AosDisks.WriteProtected THEN string := " (res: write-protected)";
  3694. ELSIF res = AosDisks.Unsupported THEN string := " (res: unsupported)";
  3695. ELSIF res = AosDisks.DeviceInUse THEN string := " (res: device in use)";
  3696. ELSIF res = AosDisks.MediaMissing THEN string := " (res: no media)";
  3697. ELSIF res = NoSpaceAvailable THEN string := " (res: no space for bootfile)";
  3698. ELSE
  3699. string := " (error: ";Strings.IntToStr(res, temp); Strings.Append(string, temp); Strings.Append(string, ")");
  3700. END;
  3701. END GetErrorMsg;
  3702. PROCEDURE GetTransferError*(dev: AosDisks.Device; op, start: LONGINT; res: WORD; VAR result: ARRAY OF CHAR);
  3703. VAR w : Streams.StringWriter;
  3704. BEGIN
  3705. NEW(w, 1024);
  3706. ASSERT((dev # NIL) & (w # NIL));
  3707. CASE op OF
  3708. AosDisks.Read: w.String("Read")
  3709. |AosDisks.Write: w.String("Write")
  3710. ELSE
  3711. w.String("I/O")
  3712. END;
  3713. w.String(" on "); w.String(dev.name); w.String(" : "); w.Int(start, 1); w.String(" failed, ");
  3714. GetErrorMsg("", res, result); w.String (result);
  3715. w.Get(result);
  3716. END GetTransferError;
  3717. PROCEDURE WriteK*(w: Streams.Writer; k: LONGINT);
  3718. VAR suffix: CHAR;
  3719. BEGIN
  3720. IF k < 10*1024 THEN suffix := "K"
  3721. ELSIF k < 10*1024*1024 THEN suffix := "M"; k := k DIV 1024
  3722. ELSE suffix := "G"; k := k DIV (1024*1024)
  3723. END;
  3724. w.Int(k, 0); w.Char(suffix); w.Char("B");
  3725. END WriteK;
  3726. PROCEDURE SetBootLoaderFile*(context: Commands.Context);
  3727. VAR file: Files.File;fileName: Files.FileName;
  3728. BEGIN
  3729. IF context.arg.GetString(fileName) THEN
  3730. file := Files.Old(fileName);
  3731. IF file # NIL THEN
  3732. BootLoaderSize := (file.Length()-1) DIV BS + 1;
  3733. COPY(fileName, BootLoaderName);
  3734. context.out.String("PartitionsLib.BootLoaderName = "); context.out.String(BootLoaderName); context.out.Ln;
  3735. context.out.String("PartitionsLib.BootLoaderSize ="); context.out.Int(BootLoaderSize,1);context.out.Ln;
  3736. ELSE
  3737. context.error.String("File not present:"); context.error.String(fileName); context.error.Ln;
  3738. END;
  3739. ELSE
  3740. context.error.String("No file name specified."); context.error.Ln;
  3741. END;
  3742. END SetBootLoaderFile;
  3743. PROCEDURE Cleanup;
  3744. BEGIN {EXCLUSIVE}
  3745. operations.Finalize; operations := NIL;
  3746. diskModel.Finalize; diskModel := NIL;
  3747. END Cleanup;
  3748. BEGIN
  3749. safe := TRUE;
  3750. NEW(diskModel); NEW(operations); NEW(infobus);
  3751. Modules.InstallTermHandler(Cleanup);
  3752. BootLoaderSize := 4;
  3753. BootLoaderName := "OBL.Bin";
  3754. END PartitionsLib.
  3755. System.Free PartitionsLib ~