Compare commits

...

206 Commits

Author SHA1 Message Date
ksherlock
5cc4d44292
Update README.md 2019-08-20 09:38:19 -04:00
ksherlock
936c9b3983
add wiki link 2019-08-20 09:36:52 -04:00
ksherlock
4cf3294c26 Create README.md 2017-07-02 21:23:59 -04:00
Kelvin Sherlock
3ae1edd5a9 cp/mv - exit if unable to read source xattr. 2017-01-21 21:22:57 -05:00
Kelvin Sherlock
a6531a0a61 add xattr rm, cp, mv 2017-01-21 21:18:54 -05:00
Kelvin Sherlock
0b7d67de2b nufx no longer needs -framework carbon 2016-08-18 12:21:18 -04:00
Kelvin Sherlock
1971c33ddf travis ci 2016-08-17 11:55:27 -04:00
Kelvin Sherlock
a40d29590a travis ci 2016-08-17 11:46:38 -04:00
Kelvin Sherlock
b4ce209d83 better linux/g++ compatibility 2016-08-16 13:06:12 -04:00
Kelvin Sherlock
3885c06698 updated fuse header includes 2016-08-16 12:12:08 -04:00
Kelvin Sherlock
184b69747a update to compile with c++11 and beyond. 2016-08-15 12:29:06 -04:00
ksherlock
5d1b81ecca v1/v2 switch-a-roo
git-svn-id: https://profuse.googlecode.com/svn/trunk@408 aa027e90-d47c-11dd-86d7-074df07e0730
2012-08-31 00:59:10 +00:00
ksherlock
d4a7107bde some NuFX code not yet checked in
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@404 aa027e90-d47c-11dd-86d7-074df07e0730
2012-08-31 00:20:20 +00:00
ksherlock
25ad5cf7f5 exception::what() const throw()
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@403 aa027e90-d47c-11dd-86d7-074df07e0730
2012-08-31 00:17:35 +00:00
ksherlock
818ab5f602 fix bug when adding new file (apfm put)
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@402 aa027e90-d47c-11dd-86d7-074df07e0730
2012-01-29 05:33:16 +00:00
ksherlock
9ad93a51a7 fix clang analyzer issues
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@401 aa027e90-d47c-11dd-86d7-074df07e0730
2012-01-29 04:42:35 +00:00
ksherlock
ef1874977b update project for osxfuse
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@400 aa027e90-d47c-11dd-86d7-074df07e0730
2011-08-27 00:33:18 +00:00
ksherlock
7508097176 Update makefile for OS X 10.7 w/ OSXFUSE instead of MacFUSE
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@399 aa027e90-d47c-11dd-86d7-074df07e0730
2011-08-26 22:48:51 +00:00
ksherlock
1dd7becb2f updated makefile
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@398 aa027e90-d47c-11dd-86d7-074df07e0730
2011-04-19 17:27:08 +00:00
ksherlock
5d219195bb Support for 2MG w/o 0 filesize field (leadlight)
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@397 aa027e90-d47c-11dd-86d7-074df07e0730
2011-04-02 18:14:27 +00:00
ksherlock
e3aea1657f rename xcode project
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@396 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-19 01:58:19 +00:00
ksherlock
a686f7d646 rename xcode project
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@395 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-19 01:57:28 +00:00
ksherlock
3af649af7c fix up xcode project
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@394 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-18 03:36:34 +00:00
ksherlock
82ecf63db6 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@393 aa027e90-d47c-11dd-86d7-074df07e0730 2011-03-18 03:07:21 +00:00
ksherlock
1fe6327ab1 updated xcode project
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@392 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-18 03:07:16 +00:00
ksherlock
b39aeba7ce git-svn-id: https://profuse.googlecode.com/svn/branches/v2@391 aa027e90-d47c-11dd-86d7-074df07e0730 2011-03-14 23:16:23 +00:00
ksherlock
8778d0dcf2 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@390 aa027e90-d47c-11dd-86d7-074df07e0730 2011-03-14 23:15:37 +00:00
ksherlock
b4db3e59ab rename ProFUSE:: -> Common::
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@389 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-14 23:08:19 +00:00
ksherlock
02e3c4c532 move around exceptions, ProFUSE -> Common
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@388 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-14 22:17:49 +00:00
ksherlock
ad6fd87c3e clean up c++ headers
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@387 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-14 15:51:53 +00:00
ksherlock
304164ecfa integrate profuse (classic)
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@386 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-13 16:02:30 +00:00
ksherlock
84e0c8e08c git-svn-id: https://profuse.googlecode.com/svn/branches/v2@385 aa027e90-d47c-11dd-86d7-074df07e0730 2011-03-13 05:44:08 +00:00
ksherlock
e6f655a2c9 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@384 aa027e90-d47c-11dd-86d7-074df07e0730 2011-03-13 05:22:02 +00:00
ksherlock
c3f02b28da sdk support for apfm
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@383 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-10 18:47:04 +00:00
ksherlock
f70b5897de conditional NUFX Support
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@382 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-09 05:58:10 +00:00
ksherlock
32bb5d79b9 added SDK support, better validation/sniffing
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@381 aa027e90-d47c-11dd-86d7-074df07e0730
2011-03-09 05:02:04 +00:00
ksherlock
5f893f78fc git-svn-id: https://profuse.googlecode.com/svn/branches/v2@380 aa027e90-d47c-11dd-86d7-074df07e0730 2011-03-09 04:59:18 +00:00
ksherlock
3a671f985c fix dc42 errors
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@367 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-28 04:44:56 +00:00
ksherlock
5930e28f54 statvfs support
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@354 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-27 02:23:03 +00:00
ksherlock
efd5e7bd98 fix parent pointer bug
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@353 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-27 01:32:35 +00:00
ksherlock
e68854ff58 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@352 aa027e90-d47c-11dd-86d7-074df07e0730 2011-02-26 03:53:22 +00:00
ksherlock
9746117f71 use smart macros to make the future easier
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@351 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-26 03:52:54 +00:00
ksherlock
89e80dcc10 use smart pointer for everything (not yet tested)
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@350 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-26 02:11:46 +00:00
ksherlock
e71ab41481 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@348 aa027e90-d47c-11dd-86d7-074df07e0730 2011-02-22 05:10:32 +00:00
ksherlock
011c3b7b44 linux header fixes
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@347 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-22 05:09:45 +00:00
ksherlock
2243d8d136 shared_ptr changes.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@346 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-22 04:57:50 +00:00
ksherlock
78d35bba08 use shared_ptr for device, cache
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@345 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-22 02:59:33 +00:00
ksherlock
8e259ef1f2 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@328 aa027e90-d47c-11dd-86d7-074df07e0730 2011-02-16 03:25:34 +00:00
ksherlock
f373fa436e updated project for xcode 4
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@327 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-16 02:43:53 +00:00
ksherlock
b07c64361d errorString() to return error as text.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@326 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-16 00:23:43 +00:00
ksherlock
539aa1310c fix new Exception throwing.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@325 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-15 04:42:14 +00:00
ksherlock
6b6ad10ba0 move typedef for clang compatibility.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@324 aa027e90-d47c-11dd-86d7-074df07e0730
2011-02-15 04:00:54 +00:00
ksherlock
06bd252ebf move back
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@323 aa027e90-d47c-11dd-86d7-074df07e0730
2010-12-15 19:56:46 +00:00
ksherlock
8bda443e0e git-svn-id: https://profuse.googlecode.com/svn/branches/v2@322 aa027e90-d47c-11dd-86d7-074df07e0730 2010-12-15 19:51:02 +00:00
ksherlock
d9c9d49872 updated makefile
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@321 aa027e90-d47c-11dd-86d7-074df07e0730
2010-12-14 21:45:25 +00:00
ksherlock
70b921ffa4 updated makefile
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@320 aa027e90-d47c-11dd-86d7-074df07e0730
2010-12-13 01:53:15 +00:00
ksherlock
98fd4a1af9 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@319 aa027e90-d47c-11dd-86d7-074df07e0730 2010-06-26 03:39:14 +00:00
ksherlock
670254034a git-svn-id: https://profuse.googlecode.com/svn/branches/v2@318 aa027e90-d47c-11dd-86d7-074df07e0730 2010-06-26 03:33:03 +00:00
ksherlock
cd1e227fc5 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@317 aa027e90-d47c-11dd-86d7-074df07e0730 2010-06-26 03:31:16 +00:00
ksherlock
1894a178d1 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@316 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 19:51:50 +00:00
ksherlock
345927d4b2 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@315 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 17:54:45 +00:00
ksherlock
fedcc06840 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@314 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 16:03:35 +00:00
ksherlock
79d8352018 fix data()
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@313 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-31 16:03:26 +00:00
ksherlock
7a327fb024 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@312 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 15:27:26 +00:00
ksherlock
96f1de7206 fix compression, add setFileSize()
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@311 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-31 15:26:03 +00:00
ksherlock
f81205b63f set _parent in create()
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@310 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-31 00:35:33 +00:00
ksherlock
6a4e4f4f7f git-svn-id: https://profuse.googlecode.com/svn/branches/v2@309 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 00:35:17 +00:00
ksherlock
911ca8637d git-svn-id: https://profuse.googlecode.com/svn/branches/v2@308 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 00:17:49 +00:00
ksherlock
1b0f3ba4f5 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@307 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 00:13:56 +00:00
ksherlock
3d8bed44b3 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@306 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 00:07:17 +00:00
ksherlock
756dd6b769 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@305 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-31 00:07:02 +00:00
ksherlock
f07a6ecd8b text writing
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@304 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-31 00:06:51 +00:00
ksherlock
e3d0514c85 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@303 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-30 21:58:06 +00:00
ksherlock
e66058320f put
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@302 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-30 21:57:55 +00:00
ksherlock
e2b20ccfba copy()
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@301 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-30 21:16:06 +00:00
ksherlock
c4ea7cc0f8 improve create(), rename(), unlink()
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@300 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-30 20:18:24 +00:00
ksherlock
2a87b480a1 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@299 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-30 20:16:57 +00:00
ksherlock
2f307b3f19 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@298 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-29 20:41:07 +00:00
ksherlock
d46406ff84 Add Read/Write flags for File::, etc instead of using bool
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@297 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-29 19:29:59 +00:00
ksherlock
7df4d4840e git-svn-id: https://profuse.googlecode.com/svn/branches/v2@296 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-29 19:29:31 +00:00
ksherlock
126327d019 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@295 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-29 18:12:32 +00:00
ksherlock
9098a442c9 fix nibble block reading.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@294 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-29 18:12:14 +00:00
ksherlock
56ee715e01 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@293 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-29 18:11:34 +00:00
ksherlock
80a0061d92 add more help
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@292 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-29 01:04:14 +00:00
ksherlock
525fe7810a git-svn-id: https://profuse.googlecode.com/svn/branches/v2@291 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-29 01:03:20 +00:00
ksherlock
7cb873bfcf git-svn-id: https://profuse.googlecode.com/svn/branches/v2@290 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-28 03:13:41 +00:00
ksherlock
d4a8391650 create()
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@289 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-28 03:13:20 +00:00
ksherlock
e4efb135ed =/== issue
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@288 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-27 16:23:24 +00:00
ksherlock
54a75af101 update makefile
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@287 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-27 16:22:12 +00:00
ksherlock
7be0fc5c56 update makefile
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@286 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-27 16:18:53 +00:00
ksherlock
10190d13a2 unlink
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@285 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-27 04:16:04 +00:00
ksherlock
0fda850b94 readOnly not corerctly set
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@284 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-27 04:15:39 +00:00
ksherlock
9cf52398a2 typo
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@283 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-27 04:15:11 +00:00
ksherlock
776475f33e pascal headers
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@282 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-26 22:19:43 +00:00
ksherlock
42c6839fcb split out pascal header files.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@281 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-26 22:15:44 +00:00
ksherlock
c4849f78e5 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@280 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-26 13:22:46 +00:00
ksherlock
4de285c9e1 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@279 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-26 02:03:29 +00:00
ksherlock
724d3c5ac3 add xcode project
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@277 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-24 22:40:51 +00:00
ksherlock
fdd622535b daemonize
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@276 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-24 22:40:28 +00:00
ksherlock
0d8ca19a1a git-svn-id: https://profuse.googlecode.com/svn/branches/v2@275 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-24 22:02:31 +00:00
ksherlock
6468724573 linux xattr
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@274 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-24 21:55:07 +00:00
ksherlock
adab6265b8 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@273 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-24 21:30:03 +00:00
ksherlock
0db1d92287 linux fixes
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@272 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-24 16:05:11 +00:00
ksherlock
e59ebb4d53 unix makefile
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@271 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-24 16:04:54 +00:00
ksherlock
d6f90048d1 remove dead code
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@270 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-24 13:26:22 +00:00
ksherlock
d1cf4ebfff git-svn-id: https://profuse.googlecode.com/svn/branches/v2@269 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-24 11:37:48 +00:00
ksherlock
5f252e4dac nibblizing
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@268 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-23 21:27:11 +00:00
ksherlock
624af11ca1 fix createBlockCache definition
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@264 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-23 18:35:23 +00:00
ksherlock
06166c2d72 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@263 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-22 18:28:52 +00:00
ksherlock
8acc47bb87 fix nasty bug
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@262 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-22 18:28:46 +00:00
ksherlock
7b80e2e089 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@261 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-22 17:36:47 +00:00
ksherlock
2e4b2961eb slight sanity checking
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@260 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-22 16:11:57 +00:00
ksherlock
ef4f906d70 more block checking
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@259 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-22 16:11:41 +00:00
ksherlock
4a98605123 fix null string error
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@258 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-22 16:10:59 +00:00
ksherlock
defd397d1b fix typo
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@257 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-22 00:51:55 +00:00
ksherlock
77653743ec git-svn-id: https://profuse.googlecode.com/svn/branches/v2@256 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-22 00:51:34 +00:00
ksherlock
1e09c72aba rename files
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@255 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-22 00:51:09 +00:00
ksherlock
895bab385c git-svn-id: https://profuse.googlecode.com/svn/branches/v2@254 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-21 23:35:12 +00:00
ksherlock
deec48b728 krunch support
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@253 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-21 23:01:32 +00:00
ksherlock
949ed1e16c Prodos errors
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@252 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-21 23:01:20 +00:00
ksherlock
433ed40922 unlink support
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@251 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-21 21:22:30 +00:00
ksherlock
a7a625dc3f no throw file open
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@250 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-21 18:51:00 +00:00
ksherlock
ca431e43f9 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@249 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-21 16:12:55 +00:00
ksherlock
c901f55b50 volume name for raw devices.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@248 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-21 01:46:53 +00:00
ksherlock
0af8ce500a git-svn-id: https://profuse.googlecode.com/svn/branches/v2@246 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-21 01:29:06 +00:00
ksherlock
cff29a4736 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@245 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-21 01:18:39 +00:00
ksherlock
91e2aaac70 BSD raw device support
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@244 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-20 23:22:30 +00:00
ksherlock
da84b5bca2 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@243 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-20 22:44:12 +00:00
ksherlock
9c95d74cba compress/uncompress text
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@242 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-20 22:43:42 +00:00
ksherlock
f530a9d748 linux support.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@241 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-20 22:31:48 +00:00
ksherlock
e7b4c96984 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@240 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-20 22:30:00 +00:00
ksherlock
d9bc18bdee git-svn-id: https://profuse.googlecode.com/svn/branches/v2@239 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-20 01:27:48 +00:00
ksherlock
9cd20f791f git-svn-id: https://profuse.googlecode.com/svn/branches/v2@238 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-20 01:25:40 +00:00
ksherlock
955986bec0 add include
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@237 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-20 01:21:55 +00:00
ksherlock
31e5f9091b fix cap. typo
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@236 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-20 01:17:59 +00:00
ksherlock
f91009d870 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@235 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-20 01:15:56 +00:00
ksherlock
bc5ffeab05 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@234 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-20 01:14:56 +00:00
ksherlock
fc7c205fe0 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@233 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 23:48:33 +00:00
ksherlock
ec9ac20528 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@232 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 23:48:27 +00:00
ksherlock
a2bea3d8f7 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@231 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 23:47:48 +00:00
ksherlock
4fe15a8e99 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@230 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 23:47:32 +00:00
ksherlock
6dd25c9be5 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@229 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 23:47:21 +00:00
ksherlock
1a27d76d67 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@227 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 20:06:11 +00:00
ksherlock
e8582d0edb git-svn-id: https://profuse.googlecode.com/svn/branches/v2@226 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 17:50:43 +00:00
ksherlock
545dd02842 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@225 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 17:50:18 +00:00
ksherlock
e53674e015 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@224 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 16:06:42 +00:00
ksherlock
77fda944ae git-svn-id: https://profuse.googlecode.com/svn/branches/v2@223 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-19 01:07:47 +00:00
ksherlock
22afe5d128 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@222 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-18 21:26:07 +00:00
ksherlock
699a6e02fb git-svn-id: https://profuse.googlecode.com/svn/branches/v2@221 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-18 20:16:57 +00:00
ksherlock
629efe6da5 block cache support.
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@220 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-18 19:59:18 +00:00
ksherlock
be7a7bafc1 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@219 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-18 19:59:00 +00:00
ksherlock
f606f8c0f5 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@218 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-18 18:39:40 +00:00
ksherlock
03c0e34201 add read support
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@217 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-18 18:38:22 +00:00
ksherlock
f13263d5ad git-svn-id: https://profuse.googlecode.com/svn/branches/v2@216 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-18 03:30:45 +00:00
ksherlock
56ff80afcc deleting dead code
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@215 aa027e90-d47c-11dd-86d7-074df07e0730
2010-05-18 03:10:35 +00:00
ksherlock
bcf6cd3890 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@214 aa027e90-d47c-11dd-86d7-074df07e0730 2010-05-18 02:58:26 +00:00
ksherlock
33f209690e linux warnings
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@212 aa027e90-d47c-11dd-86d7-074df07e0730
2010-04-01 14:38:39 +00:00
ksherlock
7b729f088f git-svn-id: https://profuse.googlecode.com/svn/branches/v2@211 aa027e90-d47c-11dd-86d7-074df07e0730 2010-03-23 04:15:12 +00:00
ksherlock
3b2bbb4e99 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@210 aa027e90-d47c-11dd-86d7-074df07e0730 2010-03-23 03:02:06 +00:00
ksherlock
ea4f0c4485 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@209 aa027e90-d47c-11dd-86d7-074df07e0730 2010-03-22 01:12:51 +00:00
ksherlock
035d802d70 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@208 aa027e90-d47c-11dd-86d7-074df07e0730 2010-03-22 00:32:27 +00:00
ksherlock
e1d01c5298 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@207 aa027e90-d47c-11dd-86d7-074df07e0730 2010-03-21 22:33:16 +00:00
ksherlock
82167f07f9 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@206 aa027e90-d47c-11dd-86d7-074df07e0730 2010-03-21 22:27:20 +00:00
ksherlock
07f1244834 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@205 aa027e90-d47c-11dd-86d7-074df07e0730 2010-03-19 18:12:22 +00:00
ksherlock
1ac9b03e47 git-svn-id: https://profuse.googlecode.com/svn/branches/v2@204 aa027e90-d47c-11dd-86d7-074df07e0730 2010-03-19 17:42:13 +00:00
ksherlock
e36eb445bf MacRoman -> Unicode
git-svn-id: https://profuse.googlecode.com/svn/trunk@63 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-15 00:04:55 +00:00
ksherlock
caea19006a clean up 64-bit warnings, use ProDOS::DateTime for time conversion.
git-svn-id: https://profuse.googlecode.com/svn/trunk@62 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-09 04:14:12 +00:00
ksherlock
0d39e94245 read support
git-svn-id: https://profuse.googlecode.com/svn/trunk@60 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-08 22:14:52 +00:00
ksherlock
dd0703bd09 Untested bitmap enhancements
git-svn-id: https://profuse.googlecode.com/svn/trunk@59 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-06 22:11:19 +00:00
ksherlock
4360263a1a Add class to handle volume bitmap
git-svn-id: https://profuse.googlecode.com/svn/trunk@58 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-04 22:16:00 +00:00
ksherlock
d296abe203 git-svn-id: https://profuse.googlecode.com/svn/trunk@57 aa027e90-d47c-11dd-86d7-074df07e0730 2009-09-04 21:20:18 +00:00
ksherlock
00b6063040 clean up pedantic errors.
git-svn-id: https://profuse.googlecode.com/svn/trunk@56 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-04 20:23:20 +00:00
ksherlock
592b56c8a5 cleanup for non-OS X
git-svn-id: https://profuse.googlecode.com/svn/trunk@53 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-04 01:49:55 +00:00
ksherlock
bd808a1d6e radical rewrite
git-svn-id: https://profuse.googlecode.com/svn/trunk@52 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-04 01:41:04 +00:00
ksherlock
8fb94c9850 renaming for c++
git-svn-id: https://profuse.googlecode.com/svn/trunk@51 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-04 00:04:49 +00:00
ksherlock
560f1b56fa c++ cleanups
git-svn-id: https://profuse.googlecode.com/svn/trunk@50 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-03 23:37:27 +00:00
ksherlock
d57f04fa0b git-svn-id: https://profuse.googlecode.com/svn/trunk@49 aa027e90-d47c-11dd-86d7-074df07e0730 2009-09-03 17:57:39 +00:00
ksherlock
bfe481b412 Add checksumming
git-svn-id: https://profuse.googlecode.com/svn/trunk@48 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-03 03:52:46 +00:00
ksherlock
2458262bac add ProDOS date/time class
git-svn-id: https://profuse.googlecode.com/svn/trunk@47 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-03 03:52:04 +00:00
ksherlock
f91b71448a create a new prodos disk image
git-svn-id: https://profuse.googlecode.com/svn/trunk@46 aa027e90-d47c-11dd-86d7-074df07e0730
2009-09-01 23:07:33 +00:00
ksherlock
6d6a988208 git-svn-id: https://profuse.googlecode.com/svn/trunk@44 aa027e90-d47c-11dd-86d7-074df07e0730 2009-05-10 16:05:04 +00:00
ksherlock
cdef6cbc04 fix dst.
git-svn-id: https://profuse.googlecode.com/svn/trunk@43 aa027e90-d47c-11dd-86d7-074df07e0730
2009-05-05 01:04:47 +00:00
ksherlock
1768d932ee user.mime_type, user.charset
git-svn-id: https://profuse.googlecode.com/svn/trunk@40 aa027e90-d47c-11dd-86d7-074df07e0730
2009-05-02 23:42:18 +00:00
ksherlock
e73d7e82ff use system 6 creator/file type logic
git-svn-id: https://profuse.googlecode.com/svn/trunk@39 aa027e90-d47c-11dd-86d7-074df07e0730
2009-04-30 03:35:44 +00:00
ksherlock
abab7d0b9e debian 5 header changes
git-svn-id: https://profuse.googlecode.com/svn/trunk@38 aa027e90-d47c-11dd-86d7-074df07e0730
2009-04-28 22:32:40 +00:00
ksherlock
465a813cca create finder info/HFS creator/type data for all files.
git-svn-id: https://profuse.googlecode.com/svn/trunk@37 aa027e90-d47c-11dd-86d7-074df07e0730
2009-04-28 03:49:16 +00:00
ksherlock
d9f6b903a8 create finder info/HFS creator/type data for all files.
git-svn-id: https://profuse.googlecode.com/svn/trunk@36 aa027e90-d47c-11dd-86d7-074df07e0730
2009-04-28 03:32:02 +00:00
ksherlock
911347ac13 add freebsd makefile
git-svn-id: https://profuse.googlecode.com/svn/trunk@32 aa027e90-d47c-11dd-86d7-074df07e0730
2009-02-01 21:53:58 +00:00
ksherlock
aa8d1b0699 sparse files, namespacing, $B0 ftype
git-svn-id: https://profuse.googlecode.com/svn/trunk@28 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-25 17:29:17 +00:00
ksherlock
23d9d31132 volume header date/time
git-svn-id: https://profuse.googlecode.com/svn/trunk@27 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-24 21:32:41 +00:00
ksherlock
0b5ef674f2 add support for dos-order sectoring.
git-svn-id: https://profuse.googlecode.com/svn/trunk@25 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-24 04:14:45 +00:00
ksherlock
b510ef3ec4 git-svn-id: https://profuse.googlecode.com/svn/trunk@24 aa027e90-d47c-11dd-86d7-074df07e0730 2009-01-23 20:50:05 +00:00
ksherlock
e971546b2a splitting up functions
git-svn-id: https://profuse.googlecode.com/svn/trunk@23 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-23 20:35:09 +00:00
ksherlock
a83fc6df28 linux compatability fixes.
git-svn-id: https://profuse.googlecode.com/svn/trunk@20 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-19 17:09:25 +00:00
ksherlock
3b72264d30 git-svn-id: https://profuse.googlecode.com/svn/trunk@18 aa027e90-d47c-11dd-86d7-074df07e0730 2009-01-17 20:49:32 +00:00
ksherlock
f6018484ce cleanups for Linux
git-svn-id: https://profuse.googlecode.com/svn/trunk@17 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-09 21:57:02 +00:00
ksherlock
4ee26d864c + conditional stat::st_birthtime
+ iosize=512
+ OS X -- mount w/ volume name
+ OS X -- auto create mount dir in /Volumes/

git-svn-id: https://profuse.googlecode.com/svn/trunk@16 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-09 01:30:53 +00:00
ksherlock
e5e91ed130 add -DHAVE_STAT_BIRTHTIME for 10.5
git-svn-id: https://profuse.googlecode.com/svn/trunk@15 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-09 01:29:06 +00:00
ksherlock
ad6b4d5c32 git-svn-id: https://profuse.googlecode.com/svn/trunk@14 aa027e90-d47c-11dd-86d7-074df07e0730 2009-01-06 22:24:57 +00:00
ksherlock
d1b16d9010 fix endless loop, less strict 2mg detection.
git-svn-id: https://profuse.googlecode.com/svn/trunk@13 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-06 21:59:42 +00:00
ksherlock
1b6a7fab18 cleaner disk copy/ 2mg support
git-svn-id: https://profuse.googlecode.com/svn/trunk@12 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-06 07:37:50 +00:00
ksherlock
193e1ab933 pedantic errors, linux makefile
git-svn-id: https://profuse.googlecode.com/svn/trunk@11 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-06 06:10:52 +00:00
ksherlock
ef3aa273b3 universal 10.4 build set to use 10.4 SDK.
git-svn-id: https://profuse.googlecode.com/svn/trunk@6 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-03 23:17:56 +00:00
ksherlock
29533054d0 add target for libfuse (not libfuse_ino64) for 10.4 compatibility.
git-svn-id: https://profuse.googlecode.com/svn/trunk@5 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-03 20:15:16 +00:00
ksherlock
bc7c5d73bb initial checkin
git-svn-id: https://profuse.googlecode.com/svn/trunk@4 aa027e90-d47c-11dd-86d7-074df07e0730
2009-01-01 03:23:02 +00:00
(no author)
90bf49881f Initial directory structure.
git-svn-id: https://profuse.googlecode.com/svn/trunk@1 aa027e90-d47c-11dd-86d7-074df07e0730
2008-12-28 01:12:58 +00:00
102 changed files with 14604 additions and 3137 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
o/

11
.travis.yml Normal file
View File

@ -0,0 +1,11 @@
language: cpp
# trusty has gcc 4.8
sudo: required
dist: trusty
compiler:
- clang++
- g++
script: make
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y fuse libfuse-dev

View File

@ -1,177 +0,0 @@
#include "BlockCache.h"
struct FileBlockCache::Entry {
FileBlockCache::Entry *next;
unsigned block;
unsigned count;
bool dirty;
uint8_t buffer[512];
};
FileBlockCache::FileBlockCache(Device *device)
{
_device = device;
// allocate initial buffer.
for (unsigned i = 0; i < 10; ++i)
{
Entry *e = new Entry;
std::memset(e, 0, sizeof(Entry));
_pages.push_back(e);
_unused.push_back(e);
}
}
FileBlockCache::~FileBlockCache()
{
std::vector<Entry *>::iterator iter;
// todo -- check if dirty.
// deallocate everything that was allocated.
for (iter = _pages.begin(); iter != _pages.end(); ++iter)
{
Entrty *e = *iter;
if (e->dirty) sync(e);
delete e;
}
}
uint8_t *FileBlockCache::acquire(unsigned block)
{
Entry *e = findEntry(block);
if (e)
{
if (++e->count == 1)
_unused.remove(e);
return e->buffer;
}
if (_unused.empty())
{
e = new Entry;
_pages.push_back(e);
}
else
{
e = _unused.pop_front();
removeEntry(e);
}
std::memset(e, 0, sizeof(Entry));
unsigned hash = hashFunction(block);
e->block = block;
e->count = 1;
e->dirty = 0;
_device->read(block, e->buffer);
_e->next = _hashMap[hash];
_hashMap[hash] = e;
return e->buffer;
}
void FileBlockCache::release(unsigned block, bool dirty)
{
Entry *e = findEntry(block);
// throw error?
if (!e) return;
if (dirty) e->dirty = true;
if (e->count == 0) return;
if (--e->count == 0)
{
_unused.push_back(e);
}
// sync if dirty??
}
void FileBlockCache::markDirty(unsigned block)
{
Entry *e = findEntry(e);
if (e && e->count) e->dirty = true;
}
Entry *FileBlockCache::findEntry(unsigned block)
{
unsigned hash = hashFunction(block);
Entry *e;
e = _hashMap[hash];
while (e && e->block != block)
{
e = e->next;
}
return e;
}
void FileBlockCache::removeEntry(Entry *e)
{
unsigned hash;
Entry *curr;
Entry *prev;
if (!e) return;
hash = hashFunction(e->block);
curr = _hashMap[hash];
if (curr == e)
{
_hashMap[hash] = e->next;
return;
}
for (;;)
{
prev = curr;
curr = curr->next;
if (!curr) break;
if (e == curr)
{
prev->next = e->next;
return;
}
}
}
unsigned FileBlockCache::hashFunction(unsigned block)
{
return block % HashEntries;
}
void FileBlockCache::sync(Entry *e)
{
if (!e) return;
if (!e->dirty) return;
_device->write(e->block, e->buffer);
e->dirty = false;
}
void FileBlockCache::sync()
{
std::vector<Entry *>::iterator iter;
for (iter = _pages.begin(); iter != _pages.end(); ++iter)
{
Entry *e = *iter;
if (e && e->dirty) sync(e);
}
}

View File

@ -1,81 +0,0 @@
#ifndef __BLOCK_CACHE_H__
#define __BLOCK_CACHE__
#include <stdint.h>
#include <vector>
#include <list>
//#include <ext/hash_map>
//typedef std::__gnu_cxx::hash_map hash_map;
namespace ProFUSE {
class BlockCache {
public:
virtual ~BlockCache();
virtual uint8_t *acquire(unsigned block) = 0;
virtual void release(unsigned block, bool dirty = false) = 0;
virtual void markDirty(unsigned block) = 0;
virtual void sync() = 0;
};
class FileBlockCache : public BlockCache {
FileBlockCache(Device *);
virtual ~FileBlockCache();
virtual uint8_t *acquire(unsigned block);
virtual void release(unsigned block, bool dirty = false);
virtual void markDirty(unsigned block);
virtual void sync();
private:
struct Entry;
enum { HashEntries = 23 };
unsigned hashFunction(unsigned);
void removeEntry(Entry *);
Entry *findEntry(unsigned block);
void sync(Entry *);
std::vector<Entry *> _pages;
std::list<Entry *> _unused;
Entry *_hashMap[HashEntries];
Device *_device;
};
class MappedBlockCache : public BlockCache
{
public:
MappedBlockCache(Device *);
virtual ~MappedBlockCache();
virtual uint8_t *acquire(unsigned block);
virtual void release(unsigned block, bool dirty = false);
virtual void markDirty(unsigned block);
virtual void sync();
private:
Device *_device;
};
}
#endif

71
Cache/BlockCache.cpp Normal file
View File

@ -0,0 +1,71 @@
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <Cache/BlockCache.h>
#include <Device/BlockDevice.h>
#include <Common/Exception.h>
#include <Common/auto.h>
using namespace Device;
BlockCache::BlockCache(BlockDevicePointer device) :
_device(device)
{
_blocks = device->blocks();
_readOnly = device->readOnly();
}
BlockCache::~BlockCache()
{
}
void BlockCache::write(unsigned block, const void *bp)
{
void *address = acquire(block);
std::memcpy(address, bp, 512);
release(block, true);
}
void BlockCache::read(unsigned block, void *bp)
{
void *address = acquire(block);
std::memcpy(bp, address, 512);
release(block, false);
}
BlockCachePointer BlockCache::Create(BlockDevicePointer device)
{
// this just calls the device virtual function to create a cache.
if (!device) return BlockCachePointer();
return device->createBlockCache();
}
void BlockCache::zeroBlock(unsigned block)
{
/*
void *address = acquire(block);
std::memset(address, 0, 512);
release(block, true);
*/
uint8_t buffer[512];
std::memset(buffer, 0, 512);
write(block, buffer);
}

62
Cache/BlockCache.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef __BLOCKCACHE_H__
#define __BLOCKCACHE_H__
#include <stdint.h>
#include <vector>
#include <Device/Device.h>
class MappedFile;
namespace Device {
enum BlockReleaseFlags {
kBlockDirty = 1,
kBlockCommitNow = 2,
kBlockReuse = 3
};
class BlockCache {
public:
static BlockCachePointer Create(BlockDevicePointer device);
virtual ~BlockCache();
bool readOnly() { return _readOnly; }
unsigned blocks() { return _blocks; }
BlockDevicePointer device() { return _device; }
virtual void sync() = 0;
virtual void write(unsigned block, const void *bp);
virtual void read(unsigned block, void *bp);
virtual void *acquire(unsigned block) = 0;
virtual void release(unsigned block, int flags) = 0 ;
virtual void markDirty(unsigned block) = 0;
virtual void zeroBlock(unsigned block);
void release(unsigned block) { release(block, 0); }
void release(unsigned block, bool dirty)
{
release(block, dirty ? kBlockDirty : 0);
}
protected:
BlockCache(BlockDevicePointer device);
BlockDevicePointer _device;
private:
unsigned _blocks;
bool _readOnly;
};
} // namespace
#endif

View File

@ -1,16 +1,17 @@
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <Device/BlockDevice.h>
#include <Device/BlockCache.h>
#include <Cache/ConcreteBlockCache.h>
#include <ProFUSE/Exception.h>
#include <ProFUSE/auto.h>
#include <Common/Exception.h>
#include <Common/auto.h>
/*
@ -50,91 +51,20 @@
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
#pragma mark -
#pragma mark BlockCache
BlockCache::~BlockCache(BlockDevice *device)
//typedef std::vector<ConcreteBlockCache::Entry *>::iterator EntryIter;
BlockCachePointer ConcreteBlockCache::Create(BlockDevicePointer device, unsigned size)
{
_device = _device;
_blocks = device->blocks();
_readOnly = device->readOnly();
}
BlockCache::~BlockCache()
{
delete _device;
}
void BlockCache::write(unsigned block, const void *bp)
{
void *address = acquire(block);
std::memcpy(address, bp, 512);
release(block, true);
}
void BlockCache::read(unsigned block, void *bp)
{
void *address = acquire(block);
std::memcpy(bp, address, 512);
release(block, false);
}
#pragma mark -
#pragma mark MappedBlockCache
MappedBlockCache::MappedBlockCache(void *data, unsigned blocks)
{
_blocks = blocks;
_data = (uint8_t *)data;
}
void MappedBlockCache::write()
{
// TODO...
}
void *MappedBlockCache::load(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::load"
//return BlockCachePointer(new ConcreteBlockCache(device, size));
// constructor must be accessible to std::make_shared...
if (block >= _blocks)
throw Exception(__METHOD__ ": Invalid block.");
return _data + block * 512;
}
void MappedBlockCache::unload(unsigned block, bool dirty)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::unload"
if (!dirty) return;
// msync must be page-size aligned.
unsigned pagesize = ::getpagesize();
unsigned offset = block * 512;
void *address = _data + offset / pagesize * pagesize;
unsigned length = offset % pagesize + 512;
if (::msync(address, length, MS_ASYNC) < 0)
{
throw POSIXException(__METHOD__ ": msync failed.", errno);
}
return MAKE_SHARED(ConcreteBlockCache, device, size);
}
#pragma mark -
#pragma mark ConcreteBlockCache
typedef std::vector<BlockCache::Entry *>::iterator EntryIter;
ConcreteBlockCache::ConcreteBlockCache(BlockDevice *device, unsigned size) :
ConcreteBlockCache::ConcreteBlockCache(BlockDevicePointer device, unsigned size) :
BlockCache(device)
{
if (size < 16) size = 16;
@ -182,7 +112,7 @@ ConcreteBlockCache::~ConcreteBlockCache()
}
ConcreteBlockCache::sync()
void ConcreteBlockCache::sync()
{
EntryIter iter;
for (iter = _buffers.begin(); iter != _buffers.end(); ++iter)
@ -201,7 +131,7 @@ ConcreteBlockCache::sync()
void ConcreteBlockCache::write(unsigned block, const void *bp)
{
FileEntry *e = findEntry();
Entry *e = findEntry(block);
if (e)
{
@ -223,6 +153,8 @@ void ConcreteBlockCache::write(unsigned block, const void *bp)
setLast(e);
}
void ConcreteBlockCache::markDirty(unsigned block)
{
Entry *e = findEntry(block);
@ -232,15 +164,23 @@ void ConcreteBlockCache::markDirty(unsigned block)
}
void ConcreteBlockCache::release(unsigned block, bool dirty)
void ConcreteBlockCache::release(unsigned block, int flags)
{
Entry *e = findEntry(block);
bool dirty = flags & (kBlockDirty | kBlockCommitNow);
if (e)
{
if (dirty) e->dirty = true;
if (dirty) e->dirty = true;
decrementCount(e);
if (flags & kBlockCommitNow)
{
_device->write(block, e->buffer);
e->dirty = false;
}
}
// error otherwise?
}
@ -270,11 +210,29 @@ unsigned ConcreteBlockCache::hashFunction(unsigned block)
return block % HashTableSize;
}
ConcreteBlockCache::Entry *ConcreteBlockCache::findEntry(unsigned block)
{
Entry *e;
unsigned hash = hashFunction(block);
e = _hashTable[hash];
while ((e) && (e->block != block))
e = e->nextHash;
return e;
}
/*
* remove a block from the hashtable
* and write to dick if dirty.
*/
void removeEntry(unsigned block)
void ConcreteBlockCache::removeEntry(unsigned block)
{
Entry *e;
Entry *prev;
@ -295,7 +253,7 @@ void removeEntry(unsigned block)
for(;;)
{
prev = e;
e = e->next;
e = e->nextHash;
if (!e) break;
if (e->block == block)
@ -347,18 +305,11 @@ void ConcreteBlockCache::decrementCount(Entry *e)
e->count = e->count - 1;
if (e->count == 0)
{
if (_last == NULL)
{
_first = _last = e;
return;
}
e->prev = _last;
_last = e;
setLast(e);
}
}
Entry *ConcreteBlockCache::newEntry(unsigned block)
ConcreteBlockCache::Entry *ConcreteBlockCache::newEntry(unsigned block)
{
Entry *e;
@ -411,4 +362,19 @@ void ConcreteBlockCache::setLast(Entry *e)
e->prev = _last;
_last->next = e;
_last = e;
}
}
void ConcreteBlockCache::setFirst(Entry *e)
{
if (_first == NULL)
{
_first = _last = e;
return;
}
e->next = _first;
_first->prev = e;
_first = e;
}

View File

@ -0,0 +1,77 @@
#ifndef __CONCRETE_BLOCK_CACHE_H__
#define __CONCRETE_BLOCK_CACHE_H__
#include <vector>
#include <Cache/BlockCache.h>
namespace Device {
class ConcreteBlockCache : public BlockCache {
public:
static BlockCachePointer Create(BlockDevicePointer device, unsigned size = 16);
virtual ~ConcreteBlockCache();
virtual void sync();
virtual void write(unsigned block, const void *vp);
virtual void *acquire(unsigned block);
virtual void release(unsigned block, int flags);
virtual void markDirty(unsigned block);
// public so make_shared can access it.
ConcreteBlockCache(BlockDevicePointer device, unsigned size);
private:
struct Entry {
unsigned block;
unsigned count;
bool dirty;
struct Entry *next;
struct Entry *prev;
struct Entry *nextHash;
uint8_t buffer[512];
};
typedef std::vector<Entry *>::iterator EntryIter;
enum { HashTableSize = 23 };
std::vector<Entry *>_buffers;
Entry *_hashTable[HashTableSize];
Entry *_first;
Entry *_last;
unsigned hashFunction(unsigned block);
Entry *findEntry(unsigned block);
void removeEntry(unsigned block);
void addEntry(Entry *);
Entry *newEntry(unsigned block);
void pushEntry(Entry *);
void setLast(Entry *);
void setFirst(Entry *);
void incrementCount(Entry *);
void decrementCount(Entry *);
};
}
#endif

139
Cache/MappedBlockCache.cpp Normal file
View File

@ -0,0 +1,139 @@
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <cstddef>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <Cache/MappedBlockCache.h>
#include <Device/BlockDevice.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
using namespace Device;
BlockCachePointer MappedBlockCache::Create(BlockDevicePointer device, void *data)
{
//return BlockCachePointer(new MappedBlockCache(device, data));
return MAKE_SHARED(MappedBlockCache, device, data);
}
MappedBlockCache::MappedBlockCache(BlockDevicePointer device, void *data) :
BlockCache(device)
{
_data = (uint8_t *)data;
_dirty = false;
}
MappedBlockCache::~MappedBlockCache()
{
if (_dirty) sync();
}
void *MappedBlockCache::acquire(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::load"
if (block >= blocks())
throw Exception(__METHOD__ ": Invalid block.");
return _data + block * 512;
}
void MappedBlockCache::release(unsigned block, int flags)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::unload"
// kBlockCommitNow implies kBlockDirty.
if (flags & kBlockCommitNow)
{
sync(block);
return;
}
if (flags & kBlockDirty) _dirty = true;
}
void MappedBlockCache::write(unsigned block, const void *vp)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::write"
if (block >= blocks())
throw Exception(__METHOD__ ": Invalid block.");
_dirty = true;
std::memcpy(_data + block * 512, vp, 512);
}
void MappedBlockCache::zeroBlock(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::zeroBlock"
if (block >= blocks())
throw Exception(__METHOD__ ": Invalid block.");
_dirty = true;
std::memset(_data + block * 512, 0, 512);
}
// sync everything.
void MappedBlockCache::sync()
{
_device->sync();
_dirty = false;
}
/*
*
* sync an individual page.
*
*/
void MappedBlockCache::sync(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "MappedBlockCache::sync"
int pagesize = ::getpagesize();
void *start = _data + block * 512;
void *end = _data + 512 + block * 512;
start = (void *)((ptrdiff_t)start / pagesize * pagesize);
end = (void *)((ptrdiff_t)end / pagesize * pagesize);
if (::msync(start, pagesize, MS_SYNC) != 0)
throw POSIX::Exception(__METHOD__ ": msync", errno);
if (start != end)
{
if (::msync(end, pagesize, MS_SYNC) != 0)
throw POSIX::Exception(__METHOD__ ": msync", errno);
}
}
void MappedBlockCache::markDirty(unsigned block)
{
_dirty = true;
}

40
Cache/MappedBlockCache.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef __MAPPED_BLOCK_CACHE_H__
#define __MAPPED_BLOCK_CACHE_H__
#include <Cache/BlockCache.h>
namespace Device {
class MappedBlockCache : public BlockCache {
public:
static BlockCachePointer Create(BlockDevicePointer device, void *data);
virtual ~MappedBlockCache();
virtual void sync();
virtual void write(unsigned block, const void *vp);
virtual void zeroBlock(unsigned block);
virtual void *acquire(unsigned block);
virtual void release(unsigned block, int flags);
virtual void markDirty(unsigned block);
// public so make_shared can access it.
MappedBlockCache(BlockDevicePointer device, void *data);
private:
void sync(unsigned block);
uint8_t *_data;
bool _dirty;
};
} // namespace
#endif

44
Common/Exception.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "Exception.h"
#include <cstdio>
#include <cstring>
Exception::Exception(const char *cp):
_error(0),
_string(cp)
{
}
Exception::Exception(const std::string& string):
_error(0),
_string(string)
{
}
Exception::Exception(const char *cp, int error):
_error(error),
_string(cp)
{
}
Exception::Exception(const std::string& string, int error):
_error(error),
_string(string)
{
}
Exception::~Exception() throw()
{
}
const char *Exception::what() const throw()
{
return _string.c_str();
}
const char *Exception::errorString()
{
return "";
}

36
Common/Exception.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef __COMMON_EXCEPTION_H__
#define __COMMON_EXCEPTION_H__
#include <string>
#include <exception>
class Exception : public std::exception
{
public:
Exception(const char *cp);
Exception(const std::string &str);
virtual ~Exception() throw ();
virtual const char *what() const throw();
virtual const char *errorString();
int error() const { return _error; }
protected:
Exception(const char *cp, int error);
Exception(const std::string& string, int error);
private:
int _error;
std::string _string;
};
#endif

View File

@ -1,6 +1,5 @@
#include <ProfUSE/Lock.h>
#include "Lock.h"
using namespace ProFUSE;
Lock::Lock()
{
@ -25,4 +24,4 @@ void Lock::unlock()
bool Lock::tryLock()
{
return pthread_mutex_trylock(&_mutex) == 0;
}
}

View File

@ -3,7 +3,6 @@
#include <pthread.h>
namespace ProFUSE {
class Lock {
public:
@ -28,6 +27,4 @@ private:
};
}
#endif

View File

@ -3,7 +3,6 @@
#include <cstddef>
namespace ProFUSE {
template <class T>
class auto_array
@ -98,7 +97,6 @@ private:
};
#endif
}
#endif

35
Common/smart_pointers.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __COMMON_SMART_POINTERS_H__
#define __COMMON_SMART_POINTERS_H__
#if 1
//C++0x
#include <memory>
#define SHARED_PTR(T) std::shared_ptr<T>
#define WEAK_PTR(T) std::weak_ptr<T>
#define MAKE_SHARED(T, ...) std::make_shared<T>(__VA_ARGS__)
#define ENABLE_SHARED_FROM_THIS(T) std::enable_shared_from_this<T>
#define STATIC_POINTER_CAST(T, ARG) std::static_pointer_cast<T>(ARG)
#define DYNAMIC_POINTER_CAST(T, ARG) std::dynamic_pointer_cast<T>(ARG)
#else
// tr1
#include <tr1/memory>
#define SHARED_PTR(T) std::tr1::shared_ptr<T>
#define WEAK_PTR(T) std::tr1::weak_ptr<T>
#define MAKE_SHARED(T, ...) std::tr1::shared_ptr<T>(new T(__VA_ARGS__))
#define ENABLE_SHARED_FROM_THIS(T) std::tr1::enable_shared_from_this<T>
#define STATIC_POINTER_CAST(T, ARG) std::tr1::static_pointer_cast<T>(ARG)
#define DYNAMIC_POINTER_CAST(T, ARG) std::tr1::dynamic_pointer_cast<T>(ARG)
#endif
#endif

14
Common/unordered_map.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __COMMON_UNORDERED_MAP_H__
#define __COMMON_UNORDERED_MAP_H__
#if 1
//c++0x
#include <unordered_map>
#define UNORDERED_MAP(...) std::unordered_map(__VA_ARGS__)
#else
// tr1
#include <tr1/unordered_map>
#define UNORDERED_MAP(...) std::tr1::unordered_map(__VA_ARGS__)
#endif
#endif

View File

@ -1,98 +0,0 @@
#include "DataWriter.h"
#include "Endian.h"
#include <cstring>
using namespace ProFUSE;
DataWriter::DataWriter(unsigned size)
{
_size = size;
_release = true;
_offset = 0;
_buffer = new uint8_t[size];
}
DataWriter::DataWriter(unsigned size, void *buffer)
{
_size = size;
_buffer = (uint8_t *)buffer;
_release = false;
_offset = 0;
}
DataWriter::~DataWriter()
{
if (_release && _buffer) delete[] _buffer;
}
void DataWriter::write8(uint8_t data)
{
_buffer[_offset] = data;
_offset += 1;
}
void DataWriter::write(const void *data, unsigned size)
{
std::memcpy(pointer(), data, size);
_offset += size;
}
DataWriterLE::DataWriterLE(unsigned size) :
DataWriter(size)
{}
DataWriterLE::DataWriterLE(unsigned size, void *buffer) :
DataWriter(size, buffer)
{}
void DataWriterLE::write16(uint16_t data)
{
LittleEndian::Write16(pointer(), data);
_offset += 2;
}
void DataWriterLE::write24(uint32_t data)
{
LittleEndian::Write24(pointer(), data);
_offset += 3;
}
void DataWriterLE::write32(uint32_t data)
{
LittleEndian::Write32(pointer(), data);
_offset += 4;
}
DataWriterBE::DataWriterBE(unsigned size) :
DataWriter(size)
{}
DataWriterBE::DataWriterBE(unsigned size, void *buffer) :
DataWriter(size, buffer)
{}
void DataWriterBE::write16(uint16_t data)
{
BigEndian::Write16(pointer(), data);
_offset += 2;
}
void DataWriterBE::write24(uint32_t data)
{
BigEndian::Write24(pointer(), data);
_offset += 3;
}
void DataWriterBE::write32(uint32_t data)
{
BigEndian::Write32(pointer(), data);
_offset += 4;
}

View File

@ -1,72 +0,0 @@
#ifndef __DATAWRITER_H__
#define __DATAWRITER_H__
#include <stdint.h>
namespace ProFUSE {
class DataWriter {
public:
DataWriter(unsigned size);
DataWriter(unsigned size, void *data);
virtual ~DataWriter();
void write8(uint8_t);
virtual void write16(uint16_t) = 0;
virtual void write24(uint32_t) = 0;
virtual void write32(uint32_t) = 0;
void write(const void *data, unsigned size);
void setOffset(unsigned o) { _offset = o; }
unsigned offset() const { return _offset; }
void forward(unsigned count) { _offset += count; }
void rewind(unsigned count) { _offset -= count; }
void *data() const { return _buffer; }
unsigned size() const { return _size; }
protected:
uint8_t *pointer() const { return _offset + _buffer; }
bool _release;
unsigned _size;
unsigned _offset;
uint8_t *_buffer;
};
class DataWriterLE : public DataWriter {
public:
DataWriterLE(unsigned);
DataWriterLE(unsigned, void *);
virtual void write8(uint8_t);
virtual void write16(uint16_t);
virtual void write24(uint32_t);
virtual void write32(uint32_t);
};
class DataWriterBE : public DataWriter {
public:
DataWriterBE(unsigned);
DataWriterBE(unsigned, void *);
virtual void write8(uint8_t);
virtual void write16(uint16_t);
virtual void write24(uint32_t);
virtual void write32(uint32_t);
};
}
#endif

486
Device/Adaptor.cpp Normal file
View File

@ -0,0 +1,486 @@
#include <cstring>
#include <cstdio>
#include <Device/Adaptor.h>
#include <Common/Exception.h>
using namespace Device;
Adaptor::~Adaptor()
{
}
POAdaptor::POAdaptor(void *address)
{
_address = (uint8_t *)address;
}
void POAdaptor::readBlock(unsigned block, void *bp)
{
std::memcpy(bp, _address + block * 512, 512);
}
void POAdaptor::writeBlock(unsigned block, const void *bp)
{
std::memcpy(_address + block * 512, bp, 512);
}
unsigned DOAdaptor::Map[] = {
0x00, 0x0e, 0x0d, 0x0c,
0x0b, 0x0a, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04,
0x03, 0x02, 0x01, 0x0f
};
DOAdaptor::DOAdaptor(void *address)
{
_address = (uint8_t *)address;
}
void DOAdaptor::readBlock(unsigned block, void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
size_t offset = track | (Map[sector+i] << 8);
std::memcpy(bp, _address + offset, 256);
bp = (uint8_t *)bp + 256;
}
}
void DOAdaptor::writeBlock(unsigned block, const void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
size_t offset = track | (Map[sector+i] << 8);
std::memcpy(_address + offset, bp, 256);
bp = (uint8_t *)bp + 256;
}
}
#pragma mark -
#pragma mark NibbleAdaptor
class CircleBuffer {
public:
CircleBuffer(void *address, unsigned length)
{
_address = (uint8_t *)address;
_length = length;
}
uint8_t operator[](unsigned i) const
{
if (i >= _length) i %= _length;
return _address[i];
}
uint8_t& operator[](unsigned i)
{
if (i >= _length) i %= _length;
return _address[i];
}
private:
uint8_t *_address;
unsigned _length;
};
uint8_t NibbleAdaptor::decode44(uint8_t x, uint8_t y)
{
return ((x << 1) | 0x01) & y;
}
std::pair<uint8_t, uint8_t> NibbleAdaptor::encode44(uint8_t val)
{
uint8_t x = (val >> 1) | 0xaa;
uint8_t y = val | 0xaa;
return std::make_pair(x,y);
}
uint8_t NibbleAdaptor::encode62(uint8_t val)
{
#undef __METHOD__
#define __METHOD__ "NibbleAdaptor::encode62"
static uint8_t table[64] = {
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
if (val > 0x3f)
throw ::Exception(__METHOD__ ": Invalid 6-2 value.");
return table[val];
}
uint8_t NibbleAdaptor::decode62(uint8_t val)
{
#undef __METHOD__
#define __METHOD__ "decode62"
// auto-generated via perl.
static uint8_t table[] = {
-1, -1, -1, -1, -1, -1, 0, 1, -1, -1, 2, 3, -1, 4, 5, 6,
-1, -1, -1, -1, -1, -1, 7, 8, -1, -1, -1, 9, 10, 11, 12, 13,
-1, -1, 14, 15, 16, 17, 18, 19, -1, 20, 21, 22, 23, 24, 25, 26,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, 28, 29, 30,
-1, -1, -1, 31, -1, -1, 32, 33, -1, 34, 35, 36, 37, 38, 39, 40,
-1, -1, -1, -1, -1, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50,
-1, -1, 51, 52, 53, 54, 55, 56, -1, 57, 58, 59, 60, 61, 62, 63
};
if ((val < 0x90) || (table[val - 0x90] == 0xff))
throw ::Exception(__METHOD__ ": Invalid 6-2 encoding.");
return table[val - 0x90];
}
static int FindByte(void *address, uint8_t c, unsigned length, unsigned offset = 0)
{
for (unsigned i = offset; i < length; ++i)
{
if ( ((uint8_t *)address)[i] == c) return i;
}
return -1;
}
/*
* Address Field:
* prologue volume track sector checksum epilogue
* D5 AA 96 XX YY XX YY XX YY XX YY DE AA EB
*/
/*
* Data Field:
* prologue user data checksum epilogue
* D5 AA AD [6+2 encoded] XX DE AA EB
*/
NibbleAdaptor::NibbleAdaptor(void *address, unsigned length)
{
#undef __METHOD__
#define __METHOD__ "NibbleAdaptor::NibbleAdaptor"
_address = (uint8_t *)address;
_length = length;
// build a map of track/sectors.
unsigned state = 0;
_index.resize(35 * 16, -1);
int offset = 0;
unsigned track = 0;
unsigned sector = 0;
unsigned volume = 0;
unsigned checksum = 0;
CircleBuffer buffer(_address, _length);
for (;;)
{
offset = FindByte(address, 0xd5, length, offset);
if (offset < 0) break;
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0x96 && buffer[offset + 11] == 0xde && buffer[offset + 12] == 0xaa)
{
volume = decode44(buffer[offset + 3], buffer[offset + 4]);
track = decode44(buffer[offset + 5], buffer[offset + 6]);
sector = decode44(buffer[offset + 7], buffer[offset + 8]);
checksum = decode44(buffer[offset + 9], buffer[offset + 10]);
if (volume ^ track ^ sector ^ checksum)
throw ::Exception(__METHOD__ ": Invalid address checksum.");
if (track > 35 || sector > 16)
throw ::Exception(__METHOD__ ": Invalid track/sector.");
offset += 3 + 8 + 3;
state = 1;
continue;
}
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad && state == 1)
{
if (_index[track * 16 + sector] != -1)
{
std::fprintf(stderr, "track %u sector %u duplicated.\n", track, sector);
}
_index[track * 16 + sector] = (offset + 3) % _length;
//offset += 3 + 342 + 1 + 3;
offset++;
state = 0;
continue;
}
offset++; //???
// ????
}
// possible wraparound.
if (state == 1)
{
offset = FindByte(address, 0xd5, length, 0);
if (offset >= 0)
{
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad)
{
_index[track * 16 + sector] = (offset + 3) % _length;
}
}
}
// now check _index for offset = -1, which means the sector/track wasn't found.
for (std::vector<unsigned>::iterator iter = _index.begin(); iter != _index.end(); ++iter)
{
if (*iter == -1)
{
int offset = distance(_index.begin(), iter);
std::fprintf(stderr, "Error: track %u sector %u missing.\n", offset / 16, offset % 16);
//throw ::Exception(__METHOD__ ": Sector missing.");
}
}
}
NibbleAdaptor::~NibbleAdaptor()
{
}
void NibbleAdaptor::readBlock(unsigned block, void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned b = (block & 0x07) << 1;
/*
* block sectors
* 0 0, 2
* 1 4, 6
* 2 8, 10
* 3 12,14
* 4 1, 3
* 5 5, 7
* 6 9, 11
* 7 13, 15
*/
unsigned sector = b >> 2;
if (sector >= 16) sector -= 15;
readTrackSector(TrackSector(track, sector), bp);
readTrackSector(TrackSector(track, sector + 1), (uint8_t *)bp + 256);
}
void NibbleAdaptor::writeBlock(unsigned block, const void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned b = (block & 0x07) << 1;
/*
* block sectors
* 0 0, 2
* 1 4, 6
* 2 8, 10
* 3 12,14
* 4 1, 3
* 5 5, 7
* 6 9, 11
* 7 13, 15
*/
unsigned sector = b >> 2;
if (sector >= 16) sector -= 15;
writeTrackSector(TrackSector(track, sector), bp);
writeTrackSector(TrackSector(track, sector + 1), (const uint8_t *)bp + 256);
}
void NibbleAdaptor::readTrackSector(TrackSector ts, void *bp)
{
#undef __METHOD__
#define __METHOD__ "NibbleAdaptor::readTrackSector"
if (ts.track > 35 || ts.sector > 16)
throw ::Exception(__METHOD__ ": Invalid track/sector.");
CircleBuffer buffer(_address, _length);
uint8_t bits[86 * 3];
uint8_t checksum = 0;
unsigned offset = _index[ts.track * 16 + ts.sector];
if (offset == -1)
{
throw ::Exception(__METHOD__ ": Missing track/sector.");
}
// first 86 bytes are in the auxbuffer, backwards.
unsigned index = offset;
for (unsigned i = 0; i < 86; ++i)
{
uint8_t x = buffer[index++];
x = decode62(x);
checksum ^= x;
uint8_t y = checksum;
/*
for (unsigned j = 0; j < 3; ++j)
{
//bits[i + j * 86] = ((y & 0x01) << 1) | ((y & 0x02) >> 1);
bits[i + j * 86] = "\x00\x01\x02\x03"[y & 0x03];
y >>= 2;
}
*/
bits[i + 86 * 0] = "\x00\x02\x01\x03"[y & 0x03];
bits[i + 86 * 1] = "\x00\x02\x01\x03"[(y >> 2) & 0x03];
bits[i + 86 * 2] = "\x00\x02\x01\x03"[(y >> 4) & 0x03];
}
for (unsigned i = 0; i < 256; ++i)
{
uint8_t x = buffer[index++];
x = decode62(x);
checksum ^= x;
uint8_t y = (checksum << 2) | bits[i];
((uint8_t *)bp)[i] = y;
}
if (checksum != decode62(buffer[index++]))
std::fprintf(stderr, "Invalid checksum on track %u, sector %u\n", ts.track, ts.sector);
//throw ::Exception(__METHOD__ ": Invalid field checksum.");
}
void NibbleAdaptor::writeTrackSector(TrackSector ts, const void *bp)
{
#undef __METHOD__
#define __METHOD__ "NibbleAdaptor::writeTrackSector"
if (ts.track > 35 || ts.sector > 16)
throw ::Exception(__METHOD__ ": Invalid track/sector.");
uint8_t auxBuffer[86];
uint8_t checksum = 0;
// create the aux buffer.
std::memset(auxBuffer, 0, sizeof(auxBuffer));
for (unsigned i = 0, j = 0, shift = 0; i < 256; ++i)
{
uint8_t x = ((const uint8_t *)bp)[i];
// grab the bottom 2 bytes and reverse them.
//uint8_t y = ((x & 0x01) << 1) | ((x & 0x02) >> 1);
uint8_t y = "\x00\x02\x01\x03"[x & 0x03];
auxBuffer[j++] |= (y << shift);
if (j == 86)
{
j = 0;
shift += 2;
}
}
unsigned offset = _index[ts.track * 16 + ts.sector];
CircleBuffer buffer(_address, _length);
// create the checksum while writing to disk..
// aux buffer
for (unsigned i = 0; i < 86; ++i)
{
uint8_t x = auxBuffer[i];
buffer[offset + i] = encode62(x ^ checksum);
checksum = x;
}
for (unsigned i = 0; i < 256; ++i)
{
uint8_t x = ((const uint8_t *)bp)[i];
x >>= 2;
buffer[offset + 86 + i] = encode62(x ^ checksum);
checksum = x;
}
buffer[offset + 342] = encode62(checksum);
}

81
Device/Adaptor.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef __DEVICE_ADAPTOR_H__
#define __DEVICE_ADAPTOR_H__
#include <utility>
#include <vector>
#include <stdint.h>
#include <Device/TrackSector.h>
namespace Device {
class Adaptor
{
public:
virtual ~Adaptor();
virtual void readBlock(unsigned block, void *bp) = 0;
virtual void writeBlock(unsigned block, const void *bp) = 0;
};
class POAdaptor : public Adaptor
{
public:
POAdaptor(void *address);
virtual void readBlock(unsigned block, void *bp);
virtual void writeBlock(unsigned block, const void *bp);
private:
uint8_t *_address;
};
class DOAdaptor : public Adaptor
{
public:
DOAdaptor(void *address);
virtual void readBlock(unsigned block, void *bp);
virtual void writeBlock(unsigned block, const void *bp);
static unsigned Map[];
private:
uint8_t *_address;
};
// TODO -- nibble adaptor.
class NibbleAdaptor : public Adaptor
{
public:
NibbleAdaptor(void *address, unsigned length);
virtual ~NibbleAdaptor();
virtual void readBlock(unsigned block, void *bp);
virtual void writeBlock(unsigned block, const void *bp);
virtual void readTrackSector(TrackSector ts, void *bp);
virtual void writeTrackSector(TrackSector ts, const void *bp);
static std::pair<uint8_t, uint8_t>encode44(uint8_t);
static uint8_t decode44(uint8_t, uint8_t);
static uint8_t encode62(uint8_t);
static uint8_t decode62(uint8_t);
private:
uint8_t *_address;
unsigned _length;
std::vector<unsigned> _index;
};
}
#endif

View File

@ -1,120 +0,0 @@
#ifndef __BLOCKCACHE_H__
#define __BLOCKCACHE_H__
#include <stdint.h>
#include <vector>
namespace Device {
class BlockDevice;
class MappedFile;
class BlockCache {
public:
BlockCache *Create(BlockDevice *device, unsigned size = 16);
virtual ~BlockCache();
bool readOnly() { return _readOnly; }
unsigned blocks() { return _blocks; }
BlockDevice *device() { return _device; }
virtual void sync() = 0;
virtual void write(unsigned block, const void *bp);
virtual void read(unsigned block, void *bp);
virtual void *acquire(unsigned block) = 0;
virtual void release(unsigned block, bool dirty) = 0;
virtual void markDirty(unsigned block) = 0;
void release(unsigned block) { release(block, false); }
protected:
BlockCache(BlockDevice *device);
BlockDevice *_device;
private
unsigned _blocks;
bool _readOnly;
};
class ConcreteBlockCache : public BlockCache {
public:
ConcreteBlockCache(BlockDevice *device, unsigned size = 16);
virtual ~ConcreteBlockCache();
virtual void sync();
virtual void write(unsigned block, const void *vp) = 0;
virtual void *acquire(unsigned block);
virtual void release(unsigned block, bool dirty);
virtual void markDirty(unsigned block);
private:
struct Entry {
unsigned block;
unsigned count;
bool dirty;
struct Entry *next;
struct Entry *prev;
struct Entry *nextHash;
uint8_t buffer[512];
};
enum { HashTableSize = 23 };
std::vector<Entry *>_buffers;
Entry *_hashTable[HashTableSize];
Entry *_first;
Entry *_last;
unsigned hashFunction(unsigned block);
Entry *findEntry(unsigned block);
void removeEntry(unsigned block);
void addEntry(Entry *);
Entry *newEntry(unsigned block);
void pushEntry(Entry *);
void setLast(Entry *);
incrementCount(Entry *);
decrementCount(Entry *);
};
class MappedBlockCache : public BlockCache {
public:
MappedBlockCache(BlockDevice *, void *data);
virtual ~MappedBlockCache();
virtual void sync() = 0;
virtual void write(unsigned block, const void *vp);
virtual void *acquire(unsigned block);
virtual void release(unsigned block, bool dirty);
virtual void markDirty(unsigned block);
private:
void *_data;
};
} // namespace
#endif

View File

@ -6,89 +6,70 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <Device/BlockDevice.h>
#include <Device/BlockCache.h>
#include <Device/MappedFile.h>
#include <Cache/ConcreteBlockCache.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
#include <Device/DiskImage.h>
#include <Device/UniversalDiskImage.h>
#include <Device/DiskCopy42Image.h>
#include <Device/DavexDiskImage.h>
#include <Device/RawDevice.h>
#ifdef HAVE_NUFX
#include <Device/SDKImage.h>
#endif
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
#pragma mark -
#pragma mark BlockDevice
BlockDevice::BlockDevice()
unsigned BlockDevice::ImageType(MappedFile *f, unsigned defv)
{
_cache = NULL;
}
BlockDevice::~BlockDevice()
{
delete _cache;
}
void BlockDevice::zeroBlock(unsigned block)
{
uint8_t bp[512];
std::memset(bp, 0, 512);
#undef __METHOD__
#define __METHOD__ "BlockDevice::ImageType"
if (UniversalDiskImage::Validate(f, std::nothrow))
return '2IMG';
if (DiskCopy42Image::Validate(f, std::nothrow))
return 'DC42';
#ifdef HAVE_NUFX
if (SDKImage::Validate(f, std::nothrow))
return 'SDK_';
#endif
if (ProDOSOrderDiskImage::Validate(f, std::nothrow))
return 'PO__';
if (DOSOrderDiskImage::Validate(f, std::nothrow))
return 'DO__';
return defv;
write(block, bp);
}
AbstractBlockCache *BlockDevice::blockCache()
{
if (!_cache)
{
_cache = createBlockCache();
}
return _cache;
}
AbstractBlockCache *BlockDevice::createBlockCache()
{
return new BlockCache(this);
}
bool BlockDevice::mapped()
{
return false;
}
void BlockDevice::sync(unsigned block)
{
sync();
}
void BlockDevice::sync(TrackSector ts)
{
sync();
}
void *BlockDevice::read(unsigned block)
{
return NULL;
}
void *BlockDevice::read(TrackSector ts)
{
return NULL;
}
#pragma mark -
#pragma mark DiskImage
unsigned DiskImage::ImageType(const char *type, unsigned defv)
unsigned BlockDevice::ImageType(const char *type, unsigned defv)
{
const char *tmp;
if (type == 0 || *type == 0) return defv;
// type could be a path, eg images/file, disk.images/file
// unix-specifix.
// basename alters the input string
tmp = std::strrchr(type, '/');
if (tmp) type = tmp + 1;
// type could be a filename, in which case we check the extension.
tmp = std::strrchr(type, '.');
if (tmp) type = tmp + 1;
@ -99,191 +80,199 @@ unsigned DiskImage::ImageType(const char *type, unsigned defv)
return '2IMG';
if (::strcasecmp(type, "2img") == 0)
return '2IMG';
if (::strcasecmp(type, "dc") == 0)
return 'DC42';
if (::strcasecmp(type, "dc42") == 0)
return 'DC42';
if (::strcasecmp(type, "po") == 0)
return 'PO__';
if (::strcasecmp(type, "dmg") == 0)
return 'PO__';
if (::strcasecmp(type, "dsk") == 0)
return 'DO__';
if (::strcasecmp(type, "do") == 0)
return 'DO__';
if (::strcasecmp(type, "dvx") == 0)
return 'DVX_';
if (::strcasecmp(type, "davex") == 0)
return 'DVX_';
/*
// not supported yet.
if (::strcasecmp(tmp, "sdk") == 0)
#ifdef HAVE_NUFX
if (::strcasecmp(type, "sdk") == 0)
return 'SDK_';
*/
if (::strcasecmp(type, "shk") == 0)
return 'SDK_';
#endif
return defv;
}
DiskImage::DiskImage(const char *name, bool readOnly) :
_file(NULL)
BlockDevicePointer BlockDevice::Open(const char *name, File::FileFlags flags, unsigned imageType)
{
_file = new MappedFile(name, readOnly);
#undef __METHOD__
#define __METHOD__ "BlockDevice::Open"
struct stat st;
std::memset(&st, 0, sizeof(st));
if (::stat(name, &st) != 0)
{
throw POSIX::Exception(__METHOD__ ": stat error", errno);
}
// /dev/xxx ignore the type.
if (S_ISBLK(st.st_mode))
return RawDevice::Open(name, flags);
MappedFile file(name, flags);
if (!imageType)
{
imageType = ImageType(&file, 'PO__');
}
switch (imageType)
{
case '2IMG':
return UniversalDiskImage::Open(&file);
case 'DC42':
return DiskCopy42Image::Open(&file);
case 'DO__':
return DOSOrderDiskImage::Open(&file);
case 'PO__':
return ProDOSOrderDiskImage::Open(&file);
case 'DVX_':
return DavexDiskImage::Open(&file);
#if HAVE_NUFX
case 'SDK_':
return SDKImage::Open(name);
#endif
}
// throw an error?
return BlockDevicePointer();
}
DiskImage::DiskImage(MappedFile *file) :
_file(file)
// return the basename, without an extension.
static std::string filename(const std::string& src)
{
unsigned start;
unsigned end;
if (src.empty()) return std::string("");
start = end = 0;
for(unsigned i = 0, l = src.length(); i < l; ++i)
{
char c = src[i];
if (c == '/') start = end = i + 1;
if (c == '.') end = i;
}
if (start == src.length()) return std::string("");
if (start == end) return src.substr(start);
return src.substr(start, end - start);
}
BlockDevicePointer BlockDevice::Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType)
{
std::string xname;
if (!imageType) imageType = ImageType(fname, 'PO__');
if (vname == NULL)
{
xname = filename(std::string(fname));
vname = xname.c_str();
}
switch(imageType)
{
case '2IMG':
return UniversalDiskImage::Create(fname, blocks);
case 'DC42':
return DiskCopy42Image::Create(fname, blocks, vname);
case 'DO__':
return DOSOrderDiskImage::Create(fname, blocks);
case 'PO__':
return ProDOSOrderDiskImage::Create(fname, blocks);
case 'DVX_':
return DavexDiskImage::Create(fname, blocks, vname);
}
return BlockDevicePointer();
}
BlockDevice::BlockDevice()
{
}
BlockDevice::~BlockDevice()
{
}
DiskImage::~DiskImage()
{
delete _file;
}
bool DiskImage::readOnly()
void BlockDevice::zeroBlock(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::readOnly"
uint8_t bp[512];
std::memset(bp, 0, 512);
if (_file) return _file->readOnly();
throw Exception(__METHOD__ ": File not set.");
write(block, bp);
}
unsigned DiskImage::blocks()
bool BlockDevice::mapped()
{
#undef __METHOD__
#define __METHOD__ "DiskImage::blocks"
if (_file) return _file->blocks();
throw Exception(__METHOD__ ": File not set.");
return false;
}
void DiskImage::read(unsigned block, void *bp)
void BlockDevice::sync(unsigned block)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::read"
if (_file) return _file->readBlock(block, bp);
throw Exception(__METHOD__ ": File not set.");
sync();
}
void DiskImage::write(unsigned block, const void *bp)
/*
void BlockDevice::sync(TrackSector ts)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::write"
if (_file) return _file->writeBlock(block, bp);
throw Exception(__METHOD__ ": File not set.");
sync();
}
*/
void DiskImage::sync()
BlockCachePointer BlockDevice::createBlockCache()
{
#undef __METHOD__
#define __METHOD__ "DiskImage::sync"
if (_file) return _file->sync();
throw Exception(__METHOD__ ": File not set.");
}
AbstractBlockCache *DiskImage::createBlockCache()
{
if (_file->encoding() == MappedFile::ProDOSOrder)
return new MappedBlockCache(_file->imageData(), _file->blocks());
return BlockDevice::createBlockCache();
}
ProDOSOrderDiskImage::ProDOSOrderDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
}
ProDOSOrderDiskImage *ProDOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = new MappedFile(name, blocks * 512);
file->setBlocks(blocks);
return new ProDOSOrderDiskImage(file);
}
ProDOSOrderDiskImage *ProDOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
return new ProDOSOrderDiskImage(file);
}
void ProDOSOrderDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
if (!f) throw Exception(__METHOD__ ": File not set.");
size_t size = f->fileSize();
if (size % 512)
throw Exception(__METHOD__ ": Invalid file format.");
f->reset();
f->setBlocks(size / 512);
}
DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
}
DOSOrderDiskImage *DOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = new MappedFile(name, blocks * 512);
file->setEncoding(MappedFile::DOSOrder);
file->setBlocks(blocks);
return new DOSOrderDiskImage(file);
}
DOSOrderDiskImage *DOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
return new DOSOrderDiskImage(file);
}
void DOSOrderDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "DOSOrderDiskImage::Validate"
if (!f) throw Exception(__METHOD__ ": File not set.");
size_t size = f->fileSize();
if (size % 512)
throw Exception(__METHOD__ ": Invalid file format.");
f->reset();
f->setEncoding(MappedFile::DOSOrder);
f->setBlocks(size / 512);
unsigned b = blocks();
unsigned size = std::max(16u, b / 16);
return ConcreteBlockCache::Create(shared_from_this(), size);
}

View File

@ -4,31 +4,45 @@
#include <stdint.h>
#include <sys/types.h>
#include <ProFUSE/Exception.h>
#include <Device/Device.h>
#include <Device/TrackSector.h>
#include <Common/Exception.h>
#include <Common/smart_pointers.h>
#include <File/File.h>
class MappedFile;
namespace Device {
class MappedFile;
class AbstractBlockCache;
class BlockDevice {
class BlockDevice : public ENABLE_SHARED_FROM_THIS(BlockDevice) {
public:
// static methods.
static unsigned ImageType(const char *type, unsigned defv = 0);
static unsigned ImageType(MappedFile *, unsigned defv = 0);
static BlockDevicePointer Open(const char *name, File::FileFlags flags, unsigned imageType = 0);
static BlockDevicePointer Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType = 0);
virtual ~BlockDevice();
virtual BlockCachePointer createBlockCache();
virtual void read(unsigned block, void *bp) = 0;
virtual void read(TrackSector ts, void *bp);
//virtual void read(TrackSector ts, void *bp) = 0
virtual void write(unsigned block, const void *bp) = 0;
virtual void write(TrackSector ts, const void *bp);
//virtual void write(TrackSector ts, const void *bp) = 0;
// direct access to mapped memory (not always possible).
virtual void *read(unsigned block);
virtual void *read(TrackSector ts);
virtual unsigned blocks() = 0;
@ -38,82 +52,18 @@ public:
virtual void sync() = 0;
virtual void sync(unsigned block);
virtual void sync(TrackSector ts);
//virtual void sync(TrackSector ts);
void zeroBlock(unsigned block);
AbstractBlockCache *blockCache();
protected:
BlockDevice();
virtual AbstractBlockCache *createBlockCache();
private:
AbstractBlockCache *_cache;
};
class DiskImage : public BlockDevice {
public:
static unsigned ImageType(const char *type, unsigned defv = 0);
virtual ~DiskImage();
virtual void read(unsigned block, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void sync();
virtual bool readOnly();
virtual unsigned blocks();
protected:
virtual AbstractBlockCache *createBlockCache();
DiskImage(MappedFile * = 0);
DiskImage(const char *name, bool readOnly);
MappedFile *file() { return _file; }
void setFile(MappedFile *);
private:
MappedFile *_file;
};
class ProDOSOrderDiskImage : public DiskImage {
public:
ProDOSOrderDiskImage(const char *name, bool readOnly);
static ProDOSOrderDiskImage *Create(const char *name, size_t blocks);
static ProDOSOrderDiskImage *Open(MappedFile *);
private:
ProDOSOrderDiskImage(MappedFile *);
static void Validate(MappedFile *);
};
class DOSOrderDiskImage : public DiskImage {
public:
DOSOrderDiskImage(const char *name, bool readOnly);
static DOSOrderDiskImage *Create(const char *name, size_t blocks);
static DOSOrderDiskImage *Open(MappedFile *);
private:
DOSOrderDiskImage(MappedFile *);
static void Validate(MappedFile *);
};
}
#endif
#endif

View File

@ -8,17 +8,17 @@
#include <algorithm>
#include <Device/DavexDiskImage.h>
#include <Device/MappedFile.h>
#include <File/MappedFile.h>
#include <Device/Adaptor.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Cache/MappedBlockCache.h>
using namespace Device;
using namespace LittleEndian;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
/*
http://www.umich.edu/~archive/apple2/technotes/ftn/FTN.E0.8004
@ -26,16 +26,17 @@ using ProFUSE::POSIXException;
static const char *IdentityCheck = "\x60VSTORE [Davex]\x00";
DavexDiskImage::DavexDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
// private, validation already performed.
DavexDiskImage::DavexDiskImage(MappedFile *f) :
DiskImage(f)
DavexDiskImage::DavexDiskImage(MappedFile *file) :
DiskImage(file)
{
// at this point, file is no longer valid.
// 512-bytes header
setBlocks((length() / 512) - 1);
setAdaptor(new POAdaptor(512 + (uint8_t *)address()));
}
@ -44,62 +45,66 @@ DavexDiskImage::~DavexDiskImage()
// scan and update usedBlocks?
}
void DavexDiskImage::Validate(MappedFile *f)
bool DavexDiskImage::Validate(MappedFile *f, const std::nothrow_t &)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Validate"
size_t size = f->length();
const void * data = f->address();
unsigned blocks = (size / 512) - 1;
if (size < 512) return false;
if (size % 512) return false;
// identity.
if (std::memcmp(data, IdentityCheck, 16))
return false;
// file format.
if (Read8(data, 0x10) != 0)
return false;
// total blocks
if (Read32(data, 33) != blocks)
return false;
// file number -- must be 1
if (Read8(data, 64) != 1)
return false;
return true;
}
bool DavexDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Validate"
size_t size = f->fileSize();
const void * data = f->fileData();
bool ok = false;
unsigned blocks = (size / 512) - 1;
if (!Validate(f, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
do {
if (size < 512) break;
if (size % 512) break;
// identity.
if (std::memcmp(data, IdentityCheck, 16))
break;
// file format.
if (Read8(data, 0x10) != 0)
break;
// total blocks
if (Read32(data, 33) != blocks)
break;
// file number -- must be 1
if (Read8(data, 64) != 1)
break;
ok = true;
} while (false);
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
f->reset();
f->setBlocks(blocks);
f->setOffset(512);
return true;
}
DavexDiskImage *DavexDiskImage::Open(MappedFile *file)
BlockDevicePointer DavexDiskImage::Open(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Open"
Validate(file);
return new DavexDiskImage(file);
//return BlockDevicePointer(new DavexDiskImage(file));
return MAKE_SHARED(DavexDiskImage, file);
}
DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks)
BlockDevicePointer DavexDiskImage::Create(const char *name, size_t blocks)
{
return Create(name, blocks, "Untitled");
}
DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks, const char *vname)
BlockDevicePointer DavexDiskImage::Create(const char *name, size_t blocks, const char *vname)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Create"
@ -109,9 +114,9 @@ DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks, const ch
IOBuffer header(tmp,512);
MappedFile *file = new MappedFile(name, blocks * 512 + 512);
MappedFile *file = MappedFile::Create(name, blocks * 512 + 512);
data = (uint8_t *)file->fileData();
data = (uint8_t *)file->address();
header.writeBytes(IdentityCheck, 16);
// file Format
@ -152,10 +157,18 @@ DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks, const ch
header.setOffset(512, true);
std::memcpy(file->fileData(), header.buffer(), 512);
std::memcpy(file->address(), header.buffer(), 512);
file->sync();
file->setOffset(512);
file->setBlocks(blocks);
return new DavexDiskImage(file);
//return BlockDevicePointer(new DavexDiskImage(file));
return MAKE_SHARED(DavexDiskImage, file);
}
BlockCachePointer DavexDiskImage::createBlockCache()
{
// need a smart pointer, but only have this....
return MappedBlockCache::Create(shared_from_this(), 512 + (uint8_t *)address());
}

View File

@ -2,8 +2,11 @@
#define __DAVEXDISKIMAGE_H__
#include <string>
#include <new>
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
namespace Device {
@ -12,18 +15,21 @@ namespace Device {
class DavexDiskImage : public DiskImage {
public:
DavexDiskImage(const char *, bool readOnly);
virtual ~DavexDiskImage();
static DavexDiskImage *Create(const char *name, size_t blocks);
static DavexDiskImage *Create(const char *name, size_t blocks, const char *vname);
static DavexDiskImage *Open(MappedFile *);
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Create(const char *name, size_t blocks, const char *vname);
static BlockDevicePointer Open(MappedFile *);
virtual BlockCachePointer createBlockCache();
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
private:
DavexDiskImage(MappedFile *);
static void Validate(MappedFile *);
private:
DavexDiskImage();
bool _changed;
std::string _volumeName;

24
Device/Device.h Normal file
View File

@ -0,0 +1,24 @@
//
// Device.h
// profuse
//
// Created by Kelvin Sherlock on 2/19/2011.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#ifndef __DEVICE_DEVICE_H__
#define __DEVICE_DEVICE_H__
#include <Common/smart_pointers.h>
namespace Device {
class BlockDevice;
class BlockCache;
typedef SHARED_PTR(BlockDevice) BlockDevicePointer;
typedef SHARED_PTR(BlockCache) BlockCachePointer;
}
#endif

View File

@ -1,204 +0,0 @@
#include <Device/DeviceReader.h>
using namespace Device;
DeviceReader::~DeviceReader()
{
}
void *DeviceReader::read(unsigned block)
{
return NULL;
}
void DeviceReader::read(TrackSector ts)
{
return NULL;
}
void DeviceReader::mapped()
{
return false;
}
#pragma mark -
#pragma mark ProDOS Order
void ProDOSOrderDeviceReader::read(unsigned block, void *bp)
{
std::memcpy(bp, read(block), 512);
}
void ProDOSOrderDeviceReader::read(TrackSector ts, void *bp)
{
std::memcpy(bp, read(ts), 256);
}
void *ProDOSOrderDeviceReader::read(unsigned block)
{
if (block > _blocks)
{
throw ProFUSE::Exception("Invalid block.");
}
return block * 512 + (uint8_t *)_data;
}
void *ProDOSOrderDeviceReader::read(TrackSector ts)
{
unsigned block = (ts.track * 16 + ts.sector) / 2;
if (block > _blocks)
{
throw ProFUSE::Exception("Invalid track/sector.");
}
return (ts.track * 16 + ts.sector) * 256 + (uint8_t *)_data;
}
void ProDOSOrderDeviceReader::write(unsigned block, const void *bp)
{
std::memcpy(read(block), bp, 512);
}
void ProDOSOrderDeviceReader::write(TrackSector ts, const void *bp)
{
std::memcpy(read(ts), bp, 256);
}
#pragma mark -
#pragma mark DOS Order
const unsigned DOSMap[] = {
0x00, 0x0e, 0x0d, 0x0c,
0x0b, 0x0a, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04,
0x03, 0x02, 0x01, 0x0f
};
void *DOSOrderDeviceReader::read(TrackSector ts)
{
if (ts.track > _tracks || ts.sector > 16)
{
throw ProFUSE::Exception("Invalid track/sector.");
}
return (ts.track * 16 + ts.sector) * 256 + (uint8_t *)_data;
}
void DOSOrderDeviceReader::read(unsigned block, void *bp)
{
TrackSector ts(block >> 3, 0);
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
ts.sector = DOSMap[sector];
std::memcpy(bp, read(ts), 256);
bp = 256 + (uint8_t *)bp;
++sector;
}
}
void DOSOrderDeviceReader::read(TrackSector ts, void *bp)
{
std::memcpy(bp, read(ts), 256);
}
void DOSOrderDeviceReader::write(unsigned block, const void *bp)
{
TrackSector ts(block >> 3, 0);
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
ts.sector = DOSMap[sector];
std::memcpy(read(ts), bp, 256);
bp = 256 + (const uint8_t *)bp;
++sector;
}
}
void DOSOrderDeviceReader::write(TrackSector ts, const void *bp)
{
std::memcpy(read(ts), bp, 256);
}
#pragma mark -
#pragma mark FileDeviceReader
FileDeviceReader::FileDeviceReader(int fd, unsigned blocks, bool readOnly)
{
_fd = fd;
_readOnly = readOnly;
_blocks = blocks;
}
bool FileDeviceReader::readOnly()
{
return _readOnly;
}
void FileDeviceReader::write(unsigned block, const void *bp)
{
off_t offset = block * 512;
size_t ok = ::pwrite(_fd, bp, 512, offset);
if (ok != 512)
throw ok < 0
? POSIXException(__METHOD__ ": Error writing block.", errno)
: Exception(__METHOD__ ": Error writing block.");
}
void FileDeviceReader::write(TrackSector ts, const void *bp)
{
off_t offset = (ts.track * 16 + ts.sector) * 256;
size_t ok = ::pwrite(_fd, bp, 256, offset);
if (ok != 256)
throw ok < 0
? POSIXException(__METHOD__ ": Error writing block.", errno)
: Exception(__METHOD__ ": Error writing block.");
}
void FileDeviceReader::read(unsigned block, void *bp)
{
off_t offset = block * 512;
size_t ok = ::pread(_fd, bp, 512, offset);
if (ok != 512)
throw ok < 0
? POSIXException(__METHOD__ ": Error reading block.", errno)
: Exception(__METHOD__ ": Error reading block.");
}
void FileDeviceReader::read(TrackSector ts, void *bp)
{
off_t offset = (ts.track * 16 + ts.sector) * 256;
size_t ok = ::pread(_fd, bp, 256, offset);
if (ok != 256)
throw ok < 0
? POSIXException(__METHOD__ ": Error reading block.", errno)
: Exception(__METHOD__ ": Error reading block.");
}

View File

@ -1,109 +0,0 @@
#ifndef __DEVICEREADER_H__
#define __DEVICEREADER_H__
#include <Device/TrackSector.h>
namespace Device {
class DeviceReader {
public:
virtual ~DeviceReader();
virtual void read(unsigned block, void *bp) = 0;
virtual void read(TrackSector ts, void *bp) = 0;
virtual void write(unsigned block, const void *bp) = 0;
virtual void write(TrackSector ts, const void *bp) = 0;
// direct access -- not always available.
virtual void *read(unsigned block);
virtual void *read(TrackSector ts);
virtual bool readOnly() = 0;
virtual bool mapped();
};
class FileDeviceReader : public DeviceReader {
// does not assume ownership of fd.
FileDeviceReader(int fd, unsigned blocks, bool readOnly);
//virtual ~FileDeviceReader();
public:
virtual bool readOnly();
virtual void read(unsigned block, void *bp);
virtual void read(TrackSector ts, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void write(TrackSector ts, const void *bp);
private:
int _fd;
unsigned _blocks;
bool _readOnly;
}
class MappedFileDeviceReader : public DeviceReader {
protected:
MappedFileDeviceReader(MappedFile *f, unsigned offset);
void *_data;
private:
MappedFile *_file
};
class ProDOSOrderDeviceReader : public MappedFileDeviceReader {
public:
virtual void read(unsigned block, void *bp);
virtual void read(TrackSector ts, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void write(TrackSector ts, const void *bp);
virtual void *read(unsigned block);
virtual void *read(TrackSector ts);
private:
unsigned blocks;
};
// 16 sectors only.
class DOSOrderDeviceReader : public MappedFileDeviceReader {
public:
virtual void read(unsigned block, void *bp);
virtual void read(TrackSector ts, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void write(TrackSector ts, const void *bp);
virtual void *read(TrackSector ts);
private:
unsigned _tracks;
};
class NibbleDeviceReader : public MappedFileDeviceReader {
private:
std::vector<unsigned> _map;
};
}
#endif

View File

@ -5,44 +5,49 @@
#include <algorithm>
#include <Device/DiskCopy42Image.h>
#include <Device/MappedFile.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Cache/MappedBlockCache.h>
using namespace Device;
using namespace BigEndian;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
enum {
oDataSize = 64,
oDataChecksum = 72,
oPrivate = 82,
oUserData = 84
};
DiskCopy42Image::DiskCopy42Image(MappedFile *f) :
DiskImage(f),
_changed(false)
{
setAdaptor(new POAdaptor(oUserData + (uint8_t *)address()));
setBlocks(Read32(address(), oDataSize) / 512);
}
DiskCopy42Image::DiskCopy42Image(const char *name, bool readOnly) :
DiskImage(name, readOnly),
_changed(false)
{
Validate(file());
}
DiskCopy42Image::~DiskCopy42Image()
{
if (_changed)
{
MappedFile *f = file();
void *data = f->fileData();
if (f)
{
uint32_t cs = Checksum(f->offset() + (uint8_t *)data,
f->blocks() * 512);
void *data = f->address();
uint32_t cs = Checksum(oUserData + (uint8_t *)data, Read32(data, oDataSize));
Write32(data, 72, cs);
Write32(data, oDataChecksum, cs);
f->sync();
}
// TODO -- checksum
@ -67,10 +72,12 @@ uint32_t DiskCopy42Image::Checksum(void *data, size_t size)
return rv;
}
DiskCopy42Image *DiskCopy42Image::Open(MappedFile *f)
BlockDevicePointer DiskCopy42Image::Open(MappedFile *f)
{
Validate(f);
return new DiskCopy42Image(f);
//return BlockDevicePointer(new DiskCopy42Image(f));
return MAKE_SHARED(DiskCopy42Image, f);
}
static uint8_t DiskFormat(size_t blocks)
@ -93,19 +100,19 @@ static uint8_t FormatByte(size_t blocks)
default: return 0x22;
}
}
DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks)
BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks)
{
return Create(name, blocks, "Untitled");
}
DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks, const char *vname)
BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks, const char *vname)
{
MappedFile *file = new MappedFile(name, blocks * 512 + 84);
file->setOffset(84);
file->setBlocks(blocks);
MappedFile *file = MappedFile::Create(name, blocks * 512 + oUserData);
uint8_t tmp[84];
IOBuffer header(tmp, 84);
uint8_t tmp[oUserData];
IOBuffer header(tmp, oUserData);
// name -- 64byte pstring.
@ -115,7 +122,7 @@ DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks, const
header.writeBytes(vname, std::min(l, 63u));
//header.resize(64);
header.setOffset(64, true);
header.setOffset(oDataSize, true);
// data size -- number of bytes
header.write32(blocks * 512);
@ -152,61 +159,82 @@ DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks, const
// private
header.write16(0x100);
std::memcpy(file->fileData(), header.buffer(), 84);
std::memcpy(file->address(), header.buffer(), oUserData);
file->sync();
return new DiskCopy42Image(file);
//return BlockDevicePointer(new DiskCopy42Image(file));
return MAKE_SHARED(DiskCopy42Image, file);
}
void DiskCopy42Image::Validate(MappedFile *file)
bool DiskCopy42Image::Validate(MappedFile *file, const std::nothrow_t &)
{
#undef __METHOD__
#define __METHOD__ "DiskCopy42Image::Validate"
size_t bytes = 0;
size_t size = file->length();
const void *data = file->address();
uint32_t checksum = 0;
if (size < oUserData)
return false;
// name must be < 64
if (Read8(data, 0) > 63)
return false;
if (Read16(data, oPrivate) != 0x100)
return false;
// bytes, not blocks.
bytes = Read32(data, oDataSize);
if (bytes % 512)
return false;
if (size < oUserData + bytes)
return false;
// todo -- checksum.
checksum = Read32(data, oDataChecksum);
uint32_t cs = Checksum(oUserData + (uint8_t *)data, bytes);
if (cs != checksum)
{
fprintf(stderr, __METHOD__ ": Warning: checksum invalid.\n");
}
return true;
}
bool DiskCopy42Image::Validate(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "DiskCopy42Image::Validate"
size_t bytes = 0;
size_t size = file->fileSize();
const void *data = file->fileData();
bool ok = false;
uint32_t checksum = 0;
if (!Validate(file, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
do {
if (size < 84) break;
// name must be < 64
if (Read8(data, 0) > 63) break;
if (Read32(data, 82) != 0x100)
break;
// bytes, not blocks.
bytes = Read32(data, 64);
if (bytes % 512) break;
if (size < 84 + bytes) break;
// todo -- checksum.
checksum = Read32(data, 72);
ok = true;
} while (false);
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
uint32_t cs = Checksum(64 + (uint8_t *)data, bytes);
if (cs != checksum)
{
fprintf(stderr, "Warning: checksum invalid.\n");
}
file->reset();
file->setOffset(64);
file->setBlocks(bytes / 512);
return true;
}
void DiskCopy42Image::write(unsigned block, const void *bp)
{
DiskImage::write(block, bp);
_changed = true;
}
BlockCachePointer DiskCopy42Image::createBlockCache()
{
// if not readonly, mark changed so crc will be updated at close.
if (!readOnly()) _changed = true;
return MappedBlockCache::Create(shared_from_this(), address());
}

View File

@ -2,6 +2,7 @@
#define __DISKCOPY42IMAGE_H__
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
#include <stdint.h>
@ -9,23 +10,31 @@ namespace Device {
class DiskCopy42Image : public DiskImage {
public:
DiskCopy42Image(const char *name, bool readOnly);
virtual ~DiskCopy42Image();
static DiskCopy42Image *Create(const char *name, size_t blocks);
static DiskCopy42Image *Create(const char *name, size_t blocks, const char *vname);
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Create(const char *name, size_t blocks, const char *vname);
static DiskCopy42Image *Open(MappedFile *);
static BlockDevicePointer Open(MappedFile *);
static uint32_t Checksum(void *data, size_t size);
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
virtual void write(unsigned block, const void *bp);
virtual BlockCachePointer createBlockCache();
DiskCopy42Image();
DiskCopy42Image(MappedFile *);
private:
DiskCopy42Image(MappedFile *);
static void Validate(MappedFile *);
bool _changed;
};

226
Device/DiskImage.cpp Normal file
View File

@ -0,0 +1,226 @@
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <Device/DiskImage.h>
#include <File/MappedFile.h>
#include <Cache/MappedBlockCache.h>
#include <Common/Exception.h>
using namespace Device;
/*
DiskImage::DiskImage(const char *name, bool readOnly)
{
File fd(name, readOnly ? O_RDONLY : O_RDWR);
MappedFile mf(fd, readOnly);
_file.adopt(mf);
_blocks = 0;
_readOnly = readOnly;
_adaptor = NULL;
}
*/
DiskImage::DiskImage(MappedFile *file)
{
_file.adopt(*file);
_blocks = 0;
_readOnly = _file.readOnly();
_adaptor = NULL;
}
DiskImage::~DiskImage()
{
delete _adaptor;
}
bool DiskImage::readOnly()
{
return _readOnly;
}
unsigned DiskImage::blocks()
{
return _blocks;
}
void DiskImage::setAdaptor(Adaptor *adaptor)
{
delete _adaptor;
_adaptor = adaptor;
}
void DiskImage::read(unsigned block, void *bp)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::read"
if (block >= _blocks)
throw ::Exception(__METHOD__ ": Invalid block.");
_adaptor->readBlock(block, bp);
}
void DiskImage::write(unsigned block, const void *bp)
{
#undef __METHOD__
#define __METHOD__ "DiskImage::write"
if (block >= _blocks)
throw ::Exception(__METHOD__ ": Invalid block.");
_adaptor->writeBlock(block, bp);
}
void DiskImage::sync()
{
#undef __METHOD__
#define __METHOD__ "DiskImage::sync"
if (_file.isValid()) return _file.sync();
throw ::Exception(__METHOD__ ": File not set.");
}
ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
// at this point, file is no longer valid.
setBlocks(length() / 512);
setAdaptor(new POAdaptor(address()));
}
BlockDevicePointer ProDOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = MappedFile::Create(name, blocks * 512);
//return BlockDevicePointer(new ProDOSOrderDiskImage(file));
return MAKE_SHARED(ProDOSOrderDiskImage, file);
}
BlockDevicePointer ProDOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
//return BlockDevicePointer(new ProDOSOrderDiskImage(file));
return MAKE_SHARED(ProDOSOrderDiskImage, file);
}
bool ProDOSOrderDiskImage::Validate(MappedFile *f, const std::nothrow_t &)
{
#undef __METHOD__
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
size_t size = f->length();
if (size % 512)
return false;
return true;
}
bool ProDOSOrderDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
if (!f || !f->isValid()) throw ::Exception(__METHOD__ ": File not set.");
if (!Validate(f, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}
BlockCachePointer ProDOSOrderDiskImage::createBlockCache()
{
return MappedBlockCache::Create(shared_from_this(), address());
}
#pragma mark -
#pragma mark DOS Order Disk Image
/*
DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
*/
DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
// at this point, file is no longer valid.
setBlocks(length() / 512);
setAdaptor(new DOAdaptor(address()));
}
BlockDevicePointer DOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = MappedFile::Create(name, blocks * 512);
//return BlockDevicePointer(new DOSOrderDiskImage(file));
return MAKE_SHARED(DOSOrderDiskImage, file);
}
BlockDevicePointer DOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
//return BlockDevicePointer(new DOSOrderDiskImage(file));
return MAKE_SHARED(DOSOrderDiskImage, file);
}
bool DOSOrderDiskImage::Validate(MappedFile *f, const std::nothrow_t &)
{
#undef __METHOD__
#define __METHOD__ "DOSOrderDiskImage::Validate"
size_t size = f->length();
if (size % 512)
return false;
return true;
}
bool DOSOrderDiskImage::Validate(MappedFile *f)
{
#undef __METHOD__
#define __METHOD__ "DOSOrderDiskImage::Validate"
if (!f || !f->isValid()) throw ::Exception(__METHOD__ ": File not set.");
if (!Validate(f, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}

97
Device/DiskImage.h Normal file
View File

@ -0,0 +1,97 @@
#ifndef __DISKIMAGE_H__
#define __DISKIMAGE_H__
#include <stdint.h>
#include <sys/types.h>
#include <Device/BlockDevice.h>
#include <Device/Adaptor.h>
#include <File/MappedFile.h>
namespace Device {
class DiskImage : public BlockDevice {
public:
virtual ~DiskImage();
virtual void read(unsigned block, void *bp);
virtual void write(unsigned block, const void *bp);
virtual void sync();
virtual bool readOnly();
virtual unsigned blocks();
protected:
DiskImage();
DiskImage(MappedFile *file = 0);
void setBlocks(unsigned blocks) { _blocks = blocks; }
void setAdaptor(Adaptor *);
void *address() const { return _file.address(); }
size_t length() const { return _file.length(); }
MappedFile *file() { return &_file; }
private:
MappedFile _file;
Adaptor *_adaptor;
bool _readOnly;
unsigned _blocks;
};
class ProDOSOrderDiskImage : public DiskImage {
public:
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Open(MappedFile *);
virtual BlockCachePointer createBlockCache();
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
ProDOSOrderDiskImage(MappedFile *);
private:
ProDOSOrderDiskImage();
};
class DOSOrderDiskImage : public DiskImage {
public:
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Open(MappedFile *);
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
DOSOrderDiskImage(MappedFile *);
private:
DOSOrderDiskImage();
};
}
#endif

View File

@ -1,253 +0,0 @@
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <Device/MappedFile.h>
#include <ProFUSE/Exception.h>
#include <ProFUSE/auto.h>
using namespace Device;
using ProFUSE::POSIXException;
using ProFUSE::Exception;
MappedFile::MappedFile(const char *name, bool readOnly)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::MappedFile"
// if unable to open as read/write, open as read-only.
ProFUSE::auto_fd fd;
if (!readOnly)
{
fd.reset(::open(name, O_RDWR));
}
if (fd < 0)
{
fd.reset(::open(name, O_RDONLY));
readOnly = true;
}
if (fd < 0)
{
throw POSIXException(__METHOD__ ": Unable to open file.", errno);
}
// init may throw; auto_fd guarantees the file will be closed if that happens.
init(fd, readOnly);
fd.release();
}
// does NOT close fd on failure.
MappedFile::MappedFile(int fd, bool readOnly)
{
init(fd, readOnly);
}
MappedFile::MappedFile(const char *name, size_t size)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::MappedFile"
_fd = -1;
_map = MAP_FAILED;
_size = size;
_readOnly = false;
_encoding = ProDOSOrder;
ProFUSE::auto_fd fd(::open(name, O_CREAT | O_TRUNC | O_RDWR, 0644));
if (fd < 0)
throw POSIXException(__METHOD__ ": Unable to create file.", errno);
// TODO -- is ftruncate portable?
if (::ftruncate(fd, _size) < 0)
{
throw POSIXException(__METHOD__ ": Unable to truncate file.", errno);
}
//_map = ::mmap(NULL, _size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, _fd, 0);
ProFUSE::auto_map map(
NULL,
_size,
PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED,
fd,
0
);
if (map == MAP_FAILED) throw POSIXException(__METHOD__ ": Unable to map file.", errno);
_fd = fd.release();
_map = map.release();
}
MappedFile::~MappedFile()
{
if (_map != MAP_FAILED) ::munmap(_map, _size);
if (_fd >= 0) ::close(_fd);
}
// does NOT close f on exception.
void MappedFile::init(int f, bool readOnly)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::init"
_fd = -1;
_map = MAP_FAILED;
_offset = 0;
_blocks = 0;
_size = 0;
_readOnly = readOnly;
_encoding = ProDOSOrder;
_size = ::lseek(f, 0, SEEK_END);
if (_size < 0)
throw POSIXException(__METHOD__ ": Unable to determine file size.", errno);
/*
_map = ::mmap(NULL, _size, readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, fd, 0);
*/
::lseek(f, 0, SEEK_SET);
ProFUSE::auto_map map(
NULL,
_size,
readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, //readOnly ? MAP_FILE : MAP_FILE | MAP_SHARED,
f,
0
);
/*
_map = ::mmap(NULL, _size, readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, f, 0);
*/
if (map == MAP_FAILED) throw POSIXException(__METHOD__ ": Unable to map file.", errno);
_fd = f;
_map = map.release();
}
const unsigned MappedFile::DOSMap[] = {
0x00, 0x0e, 0x0d, 0x0c,
0x0b, 0x0a, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04,
0x03, 0x02, 0x01, 0x0f
};
void MappedFile::readBlock(unsigned block, void *bp)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::readBlock"
if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw Exception(__METHOD__ ": Invalid address.");
switch(_encoding)
{
case ProDOSOrder:
{
size_t address = block * 512;
std::memcpy(bp, (uint8_t *)_map + _offset + address, 512);
}
break;
case DOSOrder:
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
size_t address = track | (DOSMap[sector+i] << 8);
std::memcpy(bp, (uint8_t *)_map + _offset + address, 256);
bp = (uint8_t *)bp + 256;
}
}
break;
default:
throw Exception(__METHOD__ ": Unsupported Encoding.");
}
}
void MappedFile::writeBlock(unsigned block, const void *bp)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::writeBlock"
if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (_readOnly) throw Exception(__METHOD__ ": File is readonly.");
switch(_encoding)
{
case ProDOSOrder:
{
size_t address = block * 512;
std::memcpy((uint8_t *)_map + _offset + address , bp, 512);
}
break;
case DOSOrder:
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
for (unsigned i = 0; i < 2; ++i)
{
size_t address = track | (DOSMap[sector+i] << 8);
std::memcpy((uint8_t *)_map + _offset + address, bp, 256);
bp = (uint8_t *)bp + 256;
}
}
break;
default:
throw Exception(__METHOD__ ": Unsupported Encoding.");
}
}
void MappedFile::sync()
{
#undef __METHOD__
#define __METHOD__ "MappedFile::sync"
if (_readOnly) return;
if (::msync(_map, _size, MS_SYNC) < 0)
throw POSIXException(__METHOD__ ": msync error.", errno);
}
void MappedFile::reset()
{
_offset = 0;
_blocks = 0;
_encoding = ProDOSOrder;
}

View File

@ -1,70 +0,0 @@
#ifndef __MAPPED_FILE__
#define __MAPPED_FILE__
#include <stdint.h>
#include <cstdlib>
namespace Device {
class MappedFile {
public:
enum Encoding {
ProDOSOrder = 0,
DOSOrder,
Nibblized62,
Nibblized53
};
MappedFile(const char *name, bool ReadOnly);
MappedFile(int fd, bool readOnly);
MappedFile(const char *name, size_t size);
~MappedFile();
void readBlock(unsigned block, void *bp);
void writeBlock(unsigned block, const void *bp);
void sync();
void reset();
Encoding encoding() const { return _encoding; }
void setEncoding(Encoding e) { _encoding = e; }
unsigned offset() const { return _offset; }
void setOffset(unsigned o) { _offset = o; }
unsigned blocks() const { return _blocks; }
void setBlocks(unsigned b) { _blocks = b; }
bool readOnly() const { return _readOnly; }
size_t fileSize() const { return _size; }
void *fileData() const { return _map; }
void *imageData() const { return _offset + (uint8_t *)_map; }
private:
MappedFile& operator=(const MappedFile& other);
void init(int fd, bool readOnly);
static const unsigned DOSMap[];
int _fd;
void *_map;
size_t _size;
bool _readOnly;
Encoding _encoding;
unsigned _offset;
unsigned _blocks;
};
}
#endif

View File

@ -12,7 +12,7 @@
#include <sys/disk.h>
#endif
#ifdef __LINUX__
#ifdef __linux__
#include <sys/mount.h>
#endif
@ -20,15 +20,22 @@
#include <sys/dkio.h>
#endif
#ifdef __FREEBSD__
#include <sys/disk.h>
#endif
#ifdef __minix
#include <minix/partition.h>
#endif
#include <Device/RawDevice.h>
#include <ProFUSE/auto.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
#ifdef __SUN__
void RawDevice::devSize(int fd)
@ -39,7 +46,7 @@ void RawDevice::devSize(int fd)
struct dk_minfo minfo;
if (::ioctl(fd, DKIOCGMEDIAINFO, &minfo) < 0)
throw POSIXException(__METHOD__ ": Unable to determine device size.", errno);
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
_size = minfo.dki_lbsize * minfo.dki_capacity;
_blockSize = 512; // not really, but whatever.
@ -58,11 +65,11 @@ void RawDevice::devSize(int fd)
uint64_t blockCount; // 64 bit
if (::ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize) < 0)
throw POSIXException(__METHOD__ ": Unable to determine block size.", errno);
throw POSIX::Exception(__METHOD__ ": Unable to determine block size.", errno);
if (::ioctl(fd, DKIOCGETBLOCKCOUNT, &blockCount) < 0)
throw POSIXException(__METHOD__ ": Unable to determine block count.", errno);
throw POSIX::Exception(__METHOD__ ": Unable to determine block count.", errno);
_blockSize = blockSize;
_size = _blockSize * blockCount;
@ -72,7 +79,7 @@ void RawDevice::devSize(int fd)
#endif
#ifdef __LINUX__
#ifdef __linux__
void RawDevice::devSize(int fd)
{
@ -82,7 +89,7 @@ void RawDevice::devSize(int fd)
int blocks;
if (::ioctl(fd, BLKGETSIZE, &blocks) < 0)
throw POSIXException(__METHOD__ ": Unable to determine device size.", errno);
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
_size = 512 * blocks;
_blockSize = 512; //
@ -90,48 +97,108 @@ void RawDevice::devSize(int fd)
}
#endif
// TODO -- FreeBSD/NetBSD/OpenBSD
RawDevice::RawDevice(const char *name, bool readOnly)
#ifdef __FREEBSD__
void RawDevice::devSize(int fd)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::devSize"
unsigned blockSize;
off_t mediaSize;
if (::ioctl(fd, DIOCGSECTORSIZE, &blockSize)
throw POSIX::Exception(__METHOD__ ": Unable to determine block size.", errno);
if (::ioctl(fd, DIOCGMEDIASIZE, &mediaSize)
throw POSIX::Exception(__METHOD__ ": Unable to determine media size.", errno);
_blockSize = blockSize;
_size = mediaSize;
_blocks = mediaSize / 512;
}
#endif
#ifdef __minix
void RawDevice::devSize(int fd)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::devSize"
struct partition entry;
if (::ioctl(fd, DIOCGETP, &entry) < 0)
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
_size = entry.size
_blockSize = 512; // not really but whatever.
_blocks = _size / 512;
}
#endif
RawDevice::RawDevice(const char *name, File::FileFlags flags) :
_file(name, flags)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::RawDevice"
// open read-only, verify if device is readable, and then try to upgrade to read/write?
ProFUSE::auto_fd fd;
if (!readOnly) fd.reset(::open(name, O_RDWR));
if (fd < 0)
if (!_file.isValid())
{
readOnly = false;
fd.reset(::open(name, O_RDONLY));
throw ::Exception(__METHOD__ ": Invalid file handle.");
}
if (fd < 0)
throw POSIXException(__METHOD__ ": Unable to open device.", errno);
_fd = -1;
_readOnly = readOnly;
_readOnly = flags == File::ReadOnly;
_size = 0;
_blocks = 0;
_blockSize = 0;
devSize(fd);
devSize(_file.fd());
}
RawDevice::RawDevice(File& file, File::FileFlags flags) :
_file(file)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::RawDevice"
_fd = fd.release();
if (!_file.isValid())
{
throw ::Exception(__METHOD__ ": Invalid file handle.");
}
_readOnly = flags == File::ReadOnly;
_size = 0;
_blocks = 0;
_blockSize = 0;
devSize(_file.fd());
}
RawDevice::~RawDevice()
{
if (_fd >= 0) ::close(_fd);
}
BlockDevicePointer RawDevice::Open(const char *name, File::FileFlags flags)
{
//return BlockDevicePointer(new RawDevice(name, flags));
return MAKE_SHARED(RawDevice, name, flags);
}
@ -140,20 +207,20 @@ void RawDevice::read(unsigned block, void *bp)
#undef __METHOD__
#define __METHOD__ "RawDevice::read"
if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw Exception(__METHOD__ ": Invalid address.");
if (block >= _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw ::Exception(__METHOD__ ": Invalid address.");
// sun -- use pread
// apple - read full native block(s) ?
off_t offset = block * 512;
size_t ok = ::pread(_fd, bp, 512, offset);
ssize_t ok = ::pread(_file.fd(), bp, 512, offset);
// TODO -- EINTR?
if (ok != 512)
throw ok < 0
? POSIXException(__METHOD__ ": Error reading block.", errno)
: Exception(__METHOD__ ": Error reading block.");
? POSIX::Exception(__METHOD__ ": Error reading block.", errno)
: ::Exception(__METHOD__ ": Error reading block.");
}
@ -163,20 +230,20 @@ void RawDevice::read(TrackSector ts, void *bp)
#define __METHOD__ "RawDevice::read"
unsigned block = ts.track * 8 + ts.sector / 2;
if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw Exception(__METHOD__ ": Invalid address.");
if (block >= _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
if (bp == 0) throw ::Exception(__METHOD__ ": Invalid address.");
// sun -- use pread
// apple - read full native block(s) ?
off_t offset = (ts.track * 16 + ts.sector) * 256;
size_t ok = ::pread(_fd, bp, 256, offset);
ssize_t ok = ::pread(_file.fd(), bp, 256, offset);
// TODO -- EINTR?
if (ok != 256)
throw ok < 0
? POSIXException(__METHOD__ ": Error reading block.", errno)
: Exception(__METHOD__ ": Error reading block.");
? POSIX::Exception(__METHOD__ ": Error reading block.", errno)
: ::Exception(__METHOD__ ": Error reading block.");
}
@ -185,19 +252,19 @@ void RawDevice::write(unsigned block, const void *bp)
#undef __METHOD__
#define __METHOD__ "RawDevice::write"
if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (block > _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
if (_readOnly)
throw Exception(__METHOD__ ": File is readonly.");
throw ::Exception(__METHOD__ ": File is readonly.");
off_t offset = block * 512;
size_t ok = ::pwrite(_fd, bp, 512, offset);
ssize_t ok = ::pwrite(_file.fd(), bp, 512, offset);
if (ok != 512)
throw ok < 0
? POSIXException(__METHOD__ ": Error writing block.", errno)
: Exception(__METHOD__ ": Error writing block.");
? POSIX::Exception(__METHOD__ ": Error writing block.", errno)
: ::Exception(__METHOD__ ": Error writing block.");
}
@ -207,19 +274,19 @@ void RawDevice::write(TrackSector ts, const void *bp)
#define __METHOD__ "RawDevice::write"
unsigned block = ts.track * 8 + ts.sector / 2;
if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number.");
if (block > _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
if (_readOnly)
throw Exception(__METHOD__ ": File is readonly.");
throw ::Exception(__METHOD__ ": File is readonly.");
off_t offset = (ts.track * 16 + ts.sector) * 256;
size_t ok = ::pwrite(_fd, bp, 256, offset);
ssize_t ok = ::pwrite(_file.fd(), bp, 256, offset);
if (ok != 256)
throw ok < 0
? POSIXException(__METHOD__ ": Error writing block.", errno)
: Exception(__METHOD__ ": Error writing block.");
? POSIX::Exception(__METHOD__ ": Error writing block.", errno)
: ::Exception(__METHOD__ ": Error writing block.");
}
@ -233,6 +300,13 @@ bool RawDevice::mapped()
return false;
}
unsigned RawDevice::blocks()
{
return _blocks;
}
void RawDevice::sync()
{
#undef __METHOD__
@ -240,6 +314,7 @@ void RawDevice::sync()
if (_readOnly) return;
if (::fsync(_fd) < 0)
throw POSIXException(__METHOD__ ": fsync error.", errno);
}
if (::fsync(_file.fd()) < 0)
throw POSIX::Exception(__METHOD__ ": fsync error.", errno);
}

View File

@ -5,15 +5,20 @@
#include <Device/BlockDevice.h>
#include <File/File.h>
namespace Device {
// /dev/xxx
class RawDevice : BlockDevice {
class RawDevice : public BlockDevice {
public:
RawDevice(const char *name, bool readOnly);
static BlockDevicePointer Open(const char *name, File::FileFlags flags);
virtual ~RawDevice();
@ -27,17 +32,22 @@ public:
virtual bool mapped();
virtual void sync();
private:
virtual unsigned blocks();
RawDevice(const char *name, File::FileFlags flags);
RawDevice(File& file, File::FileFlags flags);
private:
void devSize(int fd);
int _fd;
File _file;
bool _readOnly;
uint64_t _size;
unsigned _blocks;
uint64_t _size; // size of device in bytes.
unsigned _blocks; // # of 512k blocks i.e. _size / 512
unsigned _blockSize;
unsigned _blockSize; // native block size.
};
}

220
Device/SDKImage.cpp Normal file
View File

@ -0,0 +1,220 @@
//
// SDKImage.cpp
// profuse
//
// Created by Kelvin Sherlock on 3/6/2011.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#include "SDKImage.h"
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <NufxLib.h>
#include <File/File.h>
#include <File/MappedFile.h>
#include <Common/Exception.h>
#include <NuFX/Exception.h>
#include <POSIX/Exception.h>
using namespace Device;
struct record_thread
{
NuRecordIdx record_index;
NuThreadIdx thread_index;
};
static record_thread FindDiskImageThread(NuArchive *archive)
{
#undef __METHOD__
#define __METHOD__ "SDKImage::FindThread"
record_thread rt;
NuError e;
NuAttr recordCount;
e = NuGetAttr(archive, kNuAttrNumRecords, &recordCount);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuGetAttr", e);
}
for (unsigned position = 0; position < recordCount; ++position)
{
NuRecordIdx rIndex;
const NuRecord *record;
e = NuGetRecordIdxByPosition(archive, position, &rIndex);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuGetRecordIdxByPosition", e);
}
e = NuGetRecord(archive, rIndex, &record);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuGetRecord", e);
}
for (unsigned i = 0; i < NuRecordGetNumThreads(record); ++i)
{
const NuThread *thread = NuGetThread(record, i);
if (thread && NuGetThreadID(thread) == kNuThreadIDDiskImage)
{
rt.thread_index = thread->threadIdx;
rt.record_index = record->recordIdx;
return rt;
}
}
}
throw ::Exception(__METHOD__ ": not a disk image");
}
/*
* helper function to extract SDK image to /tmp and return a
* ProDOSDiskImage of the /tmp file.
*
*/
BlockDevicePointer SDKImage::Open(const char *name)
{
#undef __METHOD__
#define __METHOD__ "SDKImage::Open"
char tmp[] = "/tmp/pfuse.XXXXXXXX";
int fd = -1;
FILE *fp = NULL;
NuArchive *archive = NULL;
//const NuThread *thread = NULL;
//const NuRecord *record = NULL;
NuDataSink *sink = NULL;
//NuRecordIdx rIndex;
//NuThreadIdx tIndex;
NuError e;
record_thread rt = {0, 0};
try {
e = NuOpenRO(name, &archive);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuOpenRO", e);
}
rt = FindDiskImageThread(archive);
fd = mkstemp(tmp);
if (fd < 0)
{
throw POSIX::Exception(__METHOD__ ": mkstemp", errno);
}
fp = fdopen(fd, "w");
if (!fp)
{
::close(fd);
throw POSIX::Exception(__METHOD__ ": fdopen", errno);
}
e = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &sink);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuCreateDataSinkForFP", e);
}
e = NuExtractThread(archive, rt.thread_index, sink);
if (e)
{
throw NuFX::Exception(__METHOD__ ": NuExtractThread", e);
}
fprintf(stderr, "Extracted disk image to %s\n", tmp);
fclose(fp);
NuClose(archive);
NuFreeDataSink(sink);
fp = NULL;
archive = NULL;
sink = NULL;
}
catch(...)
{
if (fp) fclose(fp);
if (archive) NuClose(archive);
if (sink) NuFreeDataSink(sink);
throw;
}
// todo -- maybe SDKImage should extend ProDOSOrderDiskImage, have destructor
// that unklinks the temp file.
MappedFile file(tmp, File::ReadOnly);
return ProDOSOrderDiskImage::Open(&file);
}
bool SDKImage::Validate(MappedFile * f, const std::nothrow_t &)
{
// NuFile, alternating ASCII.
static const char IdentityCheck[6] = { 0x4E, 0xF5, 0x46, 0xE9, 0x6C, 0xE5 };
static const char BXYIdentityCheck[3] = { 0x0A, 0x47, 0x4C };
uint8_t *address = (uint8_t *)f->address();
size_t length = f->length();
// check for a BXY header
if (length >= 128
&& std::memcmp(address, BXYIdentityCheck, sizeof(BXYIdentityCheck)) == 0)
{
length -= 128;
address += 128;
}
if (length > sizeof(IdentityCheck)
&& std::memcmp(address, IdentityCheck, sizeof(IdentityCheck)) == 0)
return true;
return false;
}
bool SDKImage::Validate(MappedFile * f)
{
#undef __METHOD__
#define __METHOD__ "SDKImage::Validate"
if (!Validate(f, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}

30
Device/SDKImage.h Normal file
View File

@ -0,0 +1,30 @@
//
// SDKImage.h
// profuse
//
// Created by Kelvin Sherlock on 3/6/2011.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
namespace Device {
class SDKImage : public DiskImage
{
public:
static BlockDevicePointer Open(const char *name);
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
private:
SDKImage();
SDKImage(const SDKImage &);
~SDKImage();
SDKImage & operator=(const SDKImage &);
};
}

View File

@ -1,37 +1,46 @@
#include <Device/UniversalDiskImage.h>
#include <Device/MappedFile.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
#include <Cache/MappedBlockCache.h>
#include <Cache/ConcreteBlockCache.h>
using namespace Device;
using namespace LittleEndian;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
UniversalDiskImage::UniversalDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
const void *data = file()->fileData();
// flags. bit 31 = locked.
_flags = Read32(data, 0x10);
}
UniversalDiskImage::UniversalDiskImage(MappedFile *file) :
DiskImage(file)
{
_flags = 0;
// at this point, file is no longer valid.
uint8_t * data = (uint8_t *)address();
_format = Read32(data, 0x0c);
_flags = Read32(data, 0x10);
_blocks = Read32(data, 0x14);
_dataOffset = Read32(data, 0x18);
_dataLength = Read32(data, 0x1c);
setBlocks(_blocks);
// TODO -- DO, Nibble support.
setAdaptor(new POAdaptor(_dataOffset + data));
}
UniversalDiskImage *UniversalDiskImage::Create(const char *name, size_t blocks)
BlockDevicePointer UniversalDiskImage::Create(const char *name, size_t blocks)
{
// 64-byte header.
MappedFile *file = new MappedFile(name, blocks * 512 + 64);
MappedFile *file = MappedFile::Create(name, blocks * 512 + 64);
uint8_t tmp[64];
@ -65,18 +74,20 @@ UniversalDiskImage *UniversalDiskImage::Create(const char *name, size_t blocks)
// comment offset, creator, reserved -- 0.
header.setOffset(64, true);
std::memcpy(file->fileData(), header.buffer(), 64);
std::memcpy(file->address(), header.buffer(), 64);
file->setOffset(64);
file->setBlocks(blocks);
return new UniversalDiskImage(file);
//return BlockDevicePointer(new UniversalDiskImage(file));
return MAKE_SHARED(UniversalDiskImage, file);
}
UniversalDiskImage *UniversalDiskImage::Open(MappedFile *file)
BlockDevicePointer UniversalDiskImage::Open(MappedFile *file)
{
Validate(file);
return new UniversalDiskImage(file);
//return BlockDevicePointer(new UniversalDiskImage(file));
return MAKE_SHARED(UniversalDiskImage, file);
}
@ -85,47 +96,68 @@ UniversalDiskImage *UniversalDiskImage::Open(MappedFile *file)
* TODO -- honor read-only flag.
*
*/
void UniversalDiskImage::Validate(MappedFile *file)
bool UniversalDiskImage::Validate(MappedFile *file, const std::nothrow_t &)
{
#undef __METHOD__
#define __METHOD__ "UniversalDiskImage::Validate"
const void *data = file->address();
size_t size = file->length();
const void *data = file->fileData();
size_t size = file->fileSize();
bool ok = false;
unsigned blocks = 0;
unsigned offset = 0;
unsigned fileSize = 0;
do {
if (size < 64) break;
if (std::memcmp(data, "2IMG", 4)) break;
// only prodos supported, for now...
if (Read32(data, 0x0c) != 1) break;
offset = Read32(data, 0x20);
blocks = Read32(data, 0x14);
// file size == blocks * 512
if (Read32(data, 0x1c) != blocks * 512) break;
if (offset + blocks * 512 > size) break;
ok = true;
} while (false);
if (size < 64) return false;
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
if (std::memcmp(data, "2IMG", 4)) return false;
file->reset();
file->setOffset(offset);
file->setBlocks(blocks);
// only prodos supported, for now...
// TODO -- Dos Order, Nibble support.
if (Read32(data, 0x0c) != 1) return false;
blocks = Read32(data, 0x14);
offset = Read32(data, 0x18);
// file size == blocks * 512
// file size blank in some cases.
//if (Read32(data, 0x1c) != blocks * 512) return false;
fileSize = Read32(data, 0x1c);
if (fileSize != 0 && fileSize != blocks * 512) return false;
if (offset + blocks * 512 > size) return false;
return true;
}
bool UniversalDiskImage::Validate(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "UniversalDiskImage::Validate"
if (!Validate(file, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}
bool UniversalDiskImage::readOnly()
{
return (_flags & 0x8000000) || DiskImage::readOnly();
}
}
BlockCachePointer UniversalDiskImage::createBlockCache()
{
if (_format == 1)
{
return MappedBlockCache::Create(shared_from_this(), _dataOffset + (uint8_t *)address());
}
return DiskImage::createBlockCache();
}

View File

@ -3,24 +3,39 @@
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
#include <stdint.h>
namespace Device {
class UniversalDiskImage : public DiskImage {
public:
UniversalDiskImage(const char *name, bool readOnly);
static UniversalDiskImage *Create(const char *name, size_t blocks);
static UniversalDiskImage *Open(MappedFile *);
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Open(MappedFile *);
virtual bool readOnly();
private:
UniversalDiskImage(MappedFile *);
static void Validate(MappedFile *);
virtual BlockCachePointer createBlockCache();
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
UniversalDiskImage(MappedFile *);
private:
UniversalDiskImage();
uint32_t _format;
uint32_t _flags;
uint32_t _blocks;
uint32_t _dataOffset;
uint32_t _dataLength;
};
}

View File

@ -30,9 +30,9 @@
_offset += 4;
}
void writeBytes(const void *value, unsigned count)
void writeBytes(const void *src, unsigned count)
{
std::memcpy(_offset + (uint8_t *)_buffer, value, count);
std::memcpy(_offset + (uint8_t *)_buffer, src, count);
_offset += count;
}
@ -43,6 +43,42 @@
_offset += count;
}
uint8_t read8()
{
uint8_t x = Read8(_buffer, _offset);
_offset += 1;
return x;
}
uint16_t read16()
{
uint16_t x = Read16(_buffer, _offset);
_offset += 2;
return x;
}
uint32_t read24()
{
uint32_t x = Read24(_buffer, _offset);
_offset += 3;
return x;
}
uint32_t read32()
{
uint32_t x = Read32(_buffer, _offset);
_offset += 4;
return x;
}
void readBytes(void *dest, unsigned count)
{
std::memcpy(dest, _offset + (uint8_t *)_buffer, count);
_offset += count;
}
unsigned offset() const { return _offset; }
void setOffset(unsigned offset) { _offset = offset; }

View File

@ -5,11 +5,11 @@
#include <cstring>
namespace LittleEndian {
#include "IOBuffer.t.cpp"
#include "IOBuffer.cpp.h"
}
namespace BigEndian {
#include "IOBuffer.t.cpp"
#include "IOBuffer.cpp.h"
}
#endif

View File

@ -2,12 +2,8 @@
#include <cerrno>
#include <File/File.h>
#include <ProFUSE/Exception.h>
using ProFUSE::Exception;
using ProFUSE::POSIXException;
#include <Common/Exception.h>
#include <POSIX/Exception.h>
File::File()
@ -26,6 +22,22 @@ File::File(File& f)
f._fd = -1;
}
File::File(const char *name, int flags, const std::nothrow_t&)
{
_fd = ::open(name, flags);
}
File::File(const char *name, int flags, mode_t mode, const std::nothrow_t&)
{
_fd = ::open(name, flags, mode);
}
File::File(const char *name, FileFlags flags, const std::nothrow_t&)
{
_fd = ::open(name, flags == ReadOnly ? O_RDONLY : O_RDWR);
}
File::File(const char *name, int flags)
{
#undef __METHOD__
@ -33,14 +45,43 @@ File::File(const char *name, int flags)
_fd = ::open(name, flags);
if (_fd < 0)
throw POSIXException( __METHOD__ ": open", errno);
throw POSIX::Exception( __METHOD__ ": open", errno);
}
File::File(const char *name, int flags, mode_t mode)
{
#undef __METHOD__
#define __METHOD__ "File::File"
_fd = ::open(name, flags, mode);
if (_fd < 0)
throw POSIX::Exception( __METHOD__ ": open", errno);
}
File::File(const char *name, FileFlags flags)
{
#undef __METHOD__
#define __METHOD__ "File::File"
_fd = ::open(name, flags == ReadOnly ? O_RDONLY : O_RDWR);
if (_fd < 0)
throw POSIX::Exception( __METHOD__ ": open", errno);
}
File::~File()
{
close();
}
int File::release()
{
int tmp = _fd;
_fd = -1;
return tmp;
}
void File::close()
{
#undef __METHOD__
@ -48,22 +89,35 @@ void File::close()
if (_fd >= 0)
{
int fd = _fd;
::close(_fd);
_fd = -1;
if (::close(fd) != 0)
throw POSIXException(__METHOD__ ": close", errno);
// destructor shouldn't throw.
/*
if (::close(fd) != 0)
throw POSIX::Exception(__METHOD__ ": close", errno);
*/
}
}
void File::adopt(File &f)
{
if (&f == this) return;
close();
_fd = f._fd;
f._fd = -1;
}
void File::adopt(int fd)
{
if (fd == _fd) return;
close();
_fd = fd;
}
void File::swap(File &f)
{
std::swap(_fd, f._fd);

View File

@ -1,25 +1,51 @@
#ifndef __FILE_H__
#define __FILE_H__
#include <new>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
class File {
public:
enum FileFlags {
ReadOnly = 1,
ReadWrite = 2
};
File();
File(File &);
File(int fd);
File(const char *name, int flags);
File(const char *name, int flags, mode_t mode);
File(const char *name, FileFlags flags);
File(const char *name, int flags, const std::nothrow_t &);
File(const char *name, int flags, mode_t mode, const std::nothrow_t &);
File(const char *name, FileFlags flags, const std::nothrow_t &);
~File();
bool isValid() const
{
return _fd >= 0;
}
int fd() const { return _fd; }
int release();
void close();
void adopt(File &f);
void adopt(int fd);
void swap(File &f);
private:

View File

@ -4,53 +4,63 @@
#include <sys/stat.h>
#include <File/MappedFile.h>
#include <ProFUSE/Exception.h>
using ProFUSE::POSIXException;
#include <Common/Exception.h>
#include <POSIX/Exception.h>
MappedFile::MappedFile()
{
_length = -1;
_address = MAP_FAILED;
_readOnly = true;
}
MappedFile::MappedFile(MappedFile &mf)
{
_address = mf._address;
_length = mf._length;
_readOnly = mf._readOnly;
mf._address = MAP_FAILED;
mf._length = -1;
mf._readOnly = true;
}
MappedFile::MappedFile(File f, bool readOnly)
MappedFile::MappedFile(const File &f, File::FileFlags flags, size_t size)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::MappedFile"
struct stat st;
_length = -1;
_address = MAP_FAILED;
_readOnly = true;
// close enough
if (f.fd() < 0)
throw POSIXException( __METHOD__, EBADF);
if (::fstat(f.fd(), &st) != 0)
throw POSIXException(__METHOD__ ": fstat", errno);
if (!S_ISREG(st.st_mode))
throw POSIXException(__METHOD__, ENODEV);
_length = st.st_size;
_address = ::mmap(0, _length,
readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, f.fd(), 0);
if (_address == MAP_FAILED)
throw POSIXException(__METHOD__ ": mmap", errno);
init(f, flags == File::ReadOnly, size);
}
MappedFile::MappedFile(const char *name, File::FileFlags flags)
{
File f(name, flags);
_length = -1;
_address = MAP_FAILED;
_readOnly = true;
init(f, flags == File::ReadOnly, 0);
}
MappedFile::MappedFile(const char *name, File::FileFlags flags, const std::nothrow_t &nothrow)
{
File f(name, flags, nothrow);
_length = -1;
_address = MAP_FAILED;
_readOnly = true;
if (f.isValid())
init(f, flags == File::ReadOnly, 0);
}
MappedFile::~MappedFile()
{
close();
@ -58,6 +68,42 @@ MappedFile::~MappedFile()
void MappedFile::init(const File &f, bool readOnly, size_t size)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::init"
struct stat st;
int prot = readOnly ? PROT_READ : PROT_READ | PROT_WRITE;
int flags = MAP_FILE | MAP_SHARED;
// close enough
if (f.fd() < 0)
throw POSIX::Exception( __METHOD__, EBADF);
if (!size)
{
if (::fstat(f.fd(), &st) != 0)
throw POSIX::Exception(__METHOD__ ": fstat", errno);
if (!S_ISREG(st.st_mode))
throw POSIX::Exception(__METHOD__, ENODEV);
size = st.st_size;
}
_length = size;
_address = ::mmap(0, _length, prot, flags, f.fd(), 0);
if (_address == MAP_FAILED)
throw POSIX::Exception(__METHOD__ ": mmap", errno);
_readOnly = readOnly;
}
void MappedFile::close()
{
#undef __METHOD__
@ -65,14 +111,22 @@ void MappedFile::close()
if (_address != MAP_FAILED)
{
/*
void *address = _address;
size_t length = _length;
*/
::munmap(_address, _length);
_address = MAP_FAILED;
_length = -1;
_readOnly = true;
// destructor shouldn't throw.
/*
if (::munmap(address, length) != 0)
throw POSIXException(__METHOD__ ": munmap", errno);
throw POSIX::Exception(__METHOD__ ": munmap", errno);
*/
}
}
@ -84,7 +138,7 @@ void MappedFile::sync()
if (_address != MAP_FAILED)
{
if (::msync(_address, _length, MS_SYNC) != 0)
throw POSIXException(__METHOD__ ": msync", errno);
throw POSIX::Exception(__METHOD__ ": msync", errno);
}
}
@ -93,13 +147,39 @@ void MappedFile::adopt(MappedFile &mf)
close();
_address = mf._address;
_length = mf._length;
_readOnly = mf._readOnly;
mf._address = MAP_FAILED;
mf._length = -1;
mf._readOnly = true;
}
void MappedFile::swap(MappedFile &mf)
{
std::swap(_address, mf._address);
std::swap(_length, mf._length);
std::swap(_readOnly, mf._readOnly);
}
MappedFile *MappedFile::Create(const char *name, size_t size)
{
#undef __METHOD__
#define __METHOD__ "MappedFile::Create"
File fd(::open(name, O_CREAT | O_TRUNC | O_RDWR, 0644));
if (!fd.isValid())
{
throw POSIX::Exception(__METHOD__ ": Unable to create file.", errno);
}
// TODO -- is ftruncate portable?
if (::ftruncate(fd.fd(), size) < 0)
{
// TODO -- unlink?
throw POSIX::Exception(__METHOD__ ": Unable to truncate file.", errno);
}
return new MappedFile(fd, File::ReadWrite, size);
}

View File

@ -1,25 +1,39 @@
#ifndef __MAPPED_FILE_H__
#define __MAPPED_FILE_H__
#include <new>
#include <sys/mman.h>
#include <File/File.h>
class File;
class MappedFile {
public:
MappedFile();
MappedFile(File f, bool readOnly);
MappedFile(MappedFile&);
MappedFile(const File &f, File::FileFlags flags, size_t size = -1);
MappedFile(const char *name, File::FileFlags flags);
MappedFile(const char *name, File::FileFlags flags, const std::nothrow_t &nothrow);
~MappedFile();
static MappedFile *Create(const char *name, size_t size);
bool isValid() const
{
return _address != MAP_FAILED;
}
void sync();
void close();
void *address() const { return _address; }
size_t length() const { return _length; }
bool readOnly() const { return _readOnly; }
void swap(MappedFile &);
void adopt(MappedFile &);
@ -28,8 +42,11 @@ class MappedFile {
MappedFile& operator=(MappedFile &);
void init(const File &f, bool readOnly, size_t size);
void *_address;
size_t _length;
bool _readOnly;
};

298
Makefile
View File

@ -1,22 +1,280 @@
CC = g++
CPPFLAGS += -Wall -O2 -g
CC = c++
CPPFLAGS += -Wall -W -Wno-multichar -Wno-unused-parameter -Wno-unknown-pragmas -Wno-narrowing -I. -O2 -g -std=c++11
LDFLAGS += -pthread
UNAME = $(shell uname -s)
newfs_prodos: \
newfs_prodos.o \
Exception.o \
BlockDevice.o \
BlockCache.o \
UniversalDiskImage.o \
DiskCopy42Image.o \
DavexDiskImage.o \
RawDevice.o \
MappedFile.o \
Buffer.o \
Entry.o \
Directory.o \
VolumeDirectory.o \
Bitmap.o \
DateTime.o
ifeq ($(UNAME),Darwin)
# should use pkg-config but it may not be installed.
FUSE_LIBS += -losxfuse -pthread -liconv
CPPFLAGS += -I/usr/local/include/osxfuse/fuse -D_FILE_OFFSET_BITS=64 -D_DARWIN_USE_64_BIT_INODE
else
CPPFLAGS += $(shell pkg-config --cflags fuse)
FUSE_LIBS += $(shell pkg-config --libs fuse)
endif
ifdef HAVE_NUFX
DEVICE_OBJECTS += Device/SDKImage.o
EXCEPTION_OBJECTS += NuFX/Exception.o
LDFLAGS += -L/usr/local/lib/
LIBS += -lnufx -lz
CPPFLAGS += -DHAVE_NUFX=1
endif
OBJECTS += ${wildcard *.o}
OBJECTS += ${wildcard bin/*.o}
OBJECTS += ${wildcard Cache/*.o}
OBJECTS += ${wildcard Device/*.o}
OBJECTS += ${wildcard Endian/*.o}
OBJECTS += ${wildcard File/*.o}
OBJECTS += ${wildcard Pascal/*.o}
OBJECTS += ${wildcard Common/*.o}
OBJECTS += ${wildcard ProDOS/*.o}
OBJECTS += ${wildcard POSIX/*.o}
OBJECTS += ${wildcard NuFX/*.o}
TARGETS = o/apfm o/newfs_pascal o/fuse_pascal o/profuse o/xattr
BIN_OBJECTS += bin/apfm.o
BIN_OBJECTS += bin/fuse_pascal_ops.o
BIN_OBJECTS += bin/newfs_prodos.o
BIN_OBJECTS += bin/fuse_pascal.o
BIN_OBJECTS += bin/newfs_pascal.o
BIN_OBJECTS += bin/xattr.o
BIN_OBJECTS += bin/profuse.o
BIN_OBJECTS += bin/profuse_dirent.o
BIN_OBJECTS += bin/profuse_file.o
BIN_OBJECTS += bin/profuse_stat.o
BIN_OBJECTS += bin/profuse_xattr.o
CACHE_OBJECTS += Cache/BlockCache.o
CACHE_OBJECTS += Cache/ConcreteBlockCache.o
CACHE_OBJECTS += Cache/MappedBlockCache.o
DEVICE_OBJECTS += Device/Adaptor.o
DEVICE_OBJECTS += Device/BlockDevice.o
DEVICE_OBJECTS += Device/DavexDiskImage.o
DEVICE_OBJECTS += Device/DiskCopy42Image.o
DEVICE_OBJECTS += Device/DiskImage.o
DEVICE_OBJECTS += Device/RawDevice.o
DEVICE_OBJECTS += Device/UniversalDiskImage.o
ENDIAN_OBJECTS += Endian/Endian.o
FILE_OBJECTS += File/File.o
FILE_OBJECTS += File/MappedFile.o
PASCAL_OBJECTS += Pascal/Date.o
PASCAL_OBJECTS += Pascal/FileEntry.o
PASCAL_OBJECTS += Pascal/TextWriter.o
PASCAL_OBJECTS += Pascal/Entry.o
PASCAL_OBJECTS += Pascal/VolumeEntry.o
COMMON_OBJECTS += Common/Lock.o
PRODOS_OBJECTS += ProDOS/DateTime.o
PRODOS_OBJECTS += ProDOS/Disk.o
PRODOS_OBJECTS += ProDOS/File.o
EXCEPTION_OBJECTS += Common/Exception.o
EXCEPTION_OBJECTS += ProDOS/Exception.o
EXCEPTION_OBJECTS += POSIX/Exception.o
all: $(TARGETS)
apfm: o/apfm
@true
fuse_pascal: o/fuse_pascal
@true
newfs_pascal: o/newfs_pascal
@true
profuse: o/profuse
@true
xattr: o/xattr
@true
o:
mkdir $@
o/xattr: bin/xattr.o | o
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
o/newfs_pascal: bin/newfs_pascal.o \
${CACHE_OBJECTS} \
${DEVICE_OBJECTS} \
${ENDIAN_OBJECTS} \
${FILE_OBJECTS} \
${COMMON_OBJECTS} \
${EXCEPTION_OBJECTS} \
${PASCAL_OBJECTS} | o
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
o/apfm: bin/apfm.o \
${CACHE_OBJECTS} \
${DEVICE_OBJECTS} \
${ENDIAN_OBJECTS} \
${FILE_OBJECTS} \
${COMMON_OBJECTS} \
${EXCEPTION_OBJECTS} \
${PASCAL_OBJECTS} | o
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
o/fuse_pascal: bin/fuse_pascal.o bin/fuse_pascal_ops.o \
${CACHE_OBJECTS} \
${DEVICE_OBJECTS} \
${ENDIAN_OBJECTS} \
${FILE_OBJECTS} \
${COMMON_OBJECTS} \
${EXCEPTION_OBJECTS} \
${PASCAL_OBJECTS} | o
$(CC) $(LDFLAGS) $^ $(LIBS) $(FUSE_LIBS) -o $@
o/profuse: bin/profuse.o bin/profuse_dirent.o bin/profuse_file.o \
bin/profuse_stat.o bin/profuse_xattr.o \
${CACHE_OBJECTS} \
${DEVICE_OBJECTS} \
${ENDIAN_OBJECTS} \
${FILE_OBJECTS} \
${COMMON_OBJECTS} \
${EXCEPTION_OBJECTS} \
${PRODOS_OBJECTS} | o
$(CC) $(LDFLAGS) $^ $(LIBS) $(FUSE_LIBS) -o $@
clean:
rm -f ${OBJECTS} ${TARGETS}
xattr.o: bin/xattr.cpp
newfs_pascal.o: bin/newfs_pascal.cpp Device/BlockDevice.h Common/Exception.h \
Device/TrackSector.h Cache/BlockCache.h Device/RawDevice.h File/File.h \
Pascal/Pascal.h Pascal/Date.h
fuse_pascal.o: bin/fuse_pascal.cpp Pascal/Pascal.h Pascal/Date.h \
Common/Exception.h Device/BlockDevice.h Device/TrackSector.h \
Cache/BlockCache.h
fuse_pascal_ops.o: bin/fuse_pascal_ops.cpp Pascal/Pascal.h Pascal/Date.h \
Common/auto.h Common/Exception.h
apfm.o: bin/apfm.cpp Pascal/Pascal.h Pascal/Date.h Device/BlockDevice.h \
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h
File/File.o: File/File.cpp File/File.h Common/Exception.h
File/MappedFile.o: File/MappedFile.cpp File/MappedFile.h File/File.h \
Common/Exception.h
Device/Adaptor.o: Device/Adaptor.cpp Device/Adaptor.h Device/TrackSector.h \
Common/Exception.h
Device/BlockDevice.o: Device/BlockDevice.cpp Device/BlockDevice.h \
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h \
Cache/ConcreteBlockCache.h Device/DiskImage.h Device/Adaptor.h \
File/MappedFile.h File/File.h Device/UniversalDiskImage.h \
Device/DiskCopy42Image.h Device/DavexDiskImage.h Device/RawDevice.h
Device/DavexDiskImage.o: Device/DavexDiskImage.cpp \
Device/DavexDiskImage.h \
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
Cache/BlockCache.h Device/DiskImage.h Device/Adaptor.h \
File/MappedFile.h File/File.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h
Device/DiskCopy42Image.o: Device/DiskCopy42Image.cpp \
Device/DiskCopy42Image.h \
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
Cache/BlockCache.h Device/DiskImage.h Device/Adaptor.h \
File/MappedFile.h File/File.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h
Device/DiskImage.o: Device/DiskImage.cpp Device/DiskImage.h \
Common/Exception.h \
Device/BlockDevice.h Device/TrackSector.h Cache/BlockCache.h \
Device/Adaptor.h File/MappedFile.h File/File.h Cache/MappedBlockCache.h
Device/RawDevice.o: Device/RawDevice.cpp Device/RawDevice.h \
Device/BlockDevice.h \
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h File/File.h
Device/UniversalDiskImage.o: Device/UniversalDiskImage.cpp \
Device/UniversalDiskImage.h Device/BlockDevice.h Common/Exception.h \
Device/TrackSector.h Cache/BlockCache.h Device/DiskImage.h \
Device/Adaptor.h File/MappedFile.h File/File.h Endian/Endian.h \
Endian/IOBuffer.h Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h \
Cache/ConcreteBlockCache.h
Endian/Endian.o: Endian/Endian.cpp Endian/Endian.h
Cache/BlockCache.o: Cache/BlockCache.cpp Cache/BlockCache.h \
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
Common/auto.h
Cache/ConcreteBlockCache.o: Cache/ConcreteBlockCache.cpp \
Device/BlockDevice.h \
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h \
Cache/ConcreteBlockCache.h Common/auto.h
Cache/MappedBlockCache.o: Cache/MappedBlockCache.cpp \
Cache/MappedBlockCache.h \
Cache/BlockCache.h Device/BlockDevice.h Common/Exception.h \
Device/TrackSector.h
Common/Exception.o: Common/Exception.cpp Common/Exception.h
Common/Lock.o: Common/Lock.cpp Common/Lock.h
Pascal/Date.o: Pascal/Date.cpp Pascal/Date.h
Pascal/Entry.o: Pascal/Entry.cpp Pascal/Entry.h Pascal/Date.h \
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
Cache/BlockCache.h
Pascal/FileEntry.o: Pascal/FileEntry.cpp Pascal/Pascal.h Pascal/Date.h \
Pascal/Entry.h Pascal/FileEntry.h Pascal/VolumeEntry.h Common/auto.h \
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
Cache/BlockCache.h Pascal/TextWriter.h
Pascal/VolumeEntry.o: Pascal/VolumeEntry.cpp Pascal/Pascal.h Pascal/Date.h \
Pascal/Entry.h Pascal/FileEntry.h Pascal/VolumeEntry.h Common/auto.h \
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
Cache/BlockCache.h
Pascal/TextWriter.o: Pascal/TextWriter.cpp Pascal/TextWriter.h \
Pascal/FileEntry.h Pascal/Entry.h Pascal/Date.h Common/Exception.h
ProDOS/DateTime.o: ProDOS/DateTime.cpp ProDOS/DateTime.h
ProDOS/Disk.o: ProDOS/Disk.cpp ProDOS/Disk.h
ProDOS/File.o: ProDOS/File.cpp ProDOS/File.h
ProDOS/Exception.o: ProDOS/Exception.cpp ProDOS/Exception.h Common/Exception.h
NuFX/Exception.o: NuFX/Exception.cpp NuFX/Exception.h Common/Exception.h
POSIX/Exception.o: POSIX/Exception.cpp POSIX/Exception.h Common/Exception.h

View File

@ -1,61 +0,0 @@
#include <unistd.h>
#include <sys/mman.h>
#include <cerrno>
class MappedFile
{
public:
MappedFile(int fd, bool readOnly)
{
_fd = fd;
_length = ::lseek(fd, 0, SEEK_END);
_address = MAP_FAILED;
if (_length > 0)
{
_address = ::mmap(0, _length,
readOnly ? PROT_READ : PROT_READ | PROT_WRITE,
MAP_FILE, fd, 0);
}
}
MappedFile(void *address, size_t size)
{
_fd = -1;
_address = address;
_size = size;
}
~MappedFile()
{
if (_address != MAP_FAILED) ::munmap(_address, _length);
if (_fd != -1) ::close(_fd);
}
void *address() const
{
return _address;
}
size_t length()
{
return _length;
}
int sync()
{
if (::msync(_address, _length, MS_SYNC) == 0)
return 0;
return errno;
}
private:
int _fd;
void *_address;
size_t _length;
};

63
NuFX/DirectoryEntry.cpp Normal file
View File

@ -0,0 +1,63 @@
#include "DirectoryEntry.h"
#include <cstring>
using namespace NuFX;
EntryPointer DirectoryEntry::lookup(const std::string& name) const
{
EntryIterator iter;
for (iter = _children.begin(); iter != _children.end(); ++iter)
{
EntryPointer e = *iter;
if (e->name() == name) return e;
}
return EntryPointer(); // empty.
}
DirectoryEntryPointer DirectoryEntry::dir_lookup(const std::string &name)
{
EntryIterator iter;
for (iter = _children.begin(); iter != _children.end(); ++iter)
{
EntryPointer e = *iter;
if (e->name() == name)
{
// dynamic cast, will return as empty pointer if
// not a directory.
return DYNAMIC_POINTER_CAST(DirectoryEntryPointer, e);
}
}
// not found, insert it..
DirectoryEntryPointer e(new DirectoryEntryPointer(name));
VolumeEntryPointer v = volume().lock();
_children.add(e);
if (v)
{
v->addEntry(e);
}
return e;
}
#pragma mark -
#pragma mark fuse-support
EntryPointer DirectoryEntry::childAtIndex(unsigned index) const
{
if (index >= _children.size()) return EntryPointer();
return _children[index];
}

38
NuFX/DirectoryEntry.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef __NUFX_DIRECTORYENTRY_H__
#define __NUFX_DIRECTORYENTRY_H__
#include "Entry.h"
#include <vector>
#include <dirent.t>
namespace NuFX {
class DirectoryEntry : public Entry
{
public:
EntryPointer lookup(const std::string & name) const;
EntryPointer childAtIndex(unsigned index) const;
protected:
// creates directory if it does not exist.
DirectoryEntryPointer dir_lookup(const std::string &name);
private:
std::vector<EntryPointer> _children;
typedef std::vector<EntryPointer>::iterator EntryIterator;
};
}
#endif

66
NuFX/Entry.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "Entry.h"
#include "Exception.h"
using namespace NuFX;
Entry::Entry() :
_inode(0)
{
}
Entry::Entry(const std::string& name) :
_name(name), _inode(0)
{
}
Entry::~Entry()
{
}
unsigned Entry::inode() const
{
return _inode;
}
const std::string& Entry::name() const
{
return _name;
}
void Entry::setName(const std::string& name)
{
_name = name;
}
VolumeEntryWeakPointer Entry::volume() const
{
return _volume;
}
void Entry::setVolume(VolumeEntryWeakPointer volume)
{
_volume = volume;
}
int Entry::stat(struct stat *st) const
{
return -1;
}
ssize_t Entry::read(size_t size, off_t offset) const
{
return -1;
}
ssize_t Entry::listxattr(char *namebuf, size_t size, int options) const
{
return -1;
}
ssize_t Entry::getxattr(const std::string &name, void *value, size_t size, u_int32_t position, int options) const
{
return -1;
}

69
NuFX/Entry.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef __NUFX_ENTRY_H__
#define __NUFX_ENTRY_H__
#include <Common/smart_pointers.h>
#include <string>
#include <stdint.h>
#include <NufxLib.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <dirent.h>
namespace NuFX {
class DirectoryEntry;
class Entry;
class FileEntry;
class VolumeEntry;
typedef SHARED_PTR(DirectoryEntry) DirectoryEntryPointer;
typedef SHARED_PTR(Entry) EntryPointer;
typedef SHARED_PTR(FileEntry) FileEntryPointer;
typedef SHARED_PTR(VolumeEntry) VolumeEntryPointer;
typedef WEAK_PTR(Entry) EntryWeakPointer;
typedef WEAK_PTR(VolumeEntry) VolumeEntryWeakPointer;
class Entry : public ENABLE_SHARED_FROM_THIS(Entry) {
public:
virtual ~Entry();
virtual unsigned inode() const;
virtual const std::string& name() const;
// operations...
virtual int stat(VolumeEntryPointer, struct stat *) const;
virtual ssize_t read(VolumeEntryPointer, size_t size, off_t offset) const;
virtual ssize_t listxattr(VolumeEntryPointer, char *namebuf, size_t size, int options) const;
virtual ssize_t getxattr(VolumeEntryPointer, const std::string &name, void *value, size_t size, u_int32_t position, int options) const;
virtual int open(VolumeEntryPointer, int flags);
virtual int close(VolumeEntryPointer);
protected:
Entry();
Entry(const std::string& name);
void setName(const std::string&);
private:
Entry(const Entry&);
Entry& operator=(const Entry&);
friend VolumeEntry;
std::string _name;
unsigned _inode;
};
}
#endif

10
NuFX/Exception.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "Exception.h"
namespace NuFX {
const char *NuFX::Exception::errorString()
{
return ::NuStrError((NuError)error());
}
}

35
NuFX/Exception.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __NUFX_EXCEPTION_H__
#define __NUFX_EXCEPTION_H__
#include <Common/Exception.h>
#include <NufxLib.h>
namespace NuFX {
class Exception : public ::Exception
{
public:
Exception(const char *cp, NuError error);
Exception(const std::string& string, NuError error);
virtual const char *errorString();
private:
typedef ::Exception super;
};
inline Exception::Exception(const char *cp, NuError error) :
super(cp, error)
{
}
inline Exception::Exception(const std::string& string, NuError error) :
super(string, error)
{
}
}
#endif

23
NuFX/FileEntry.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef __NUFX_FILEENTRY_H__
#define __NUFX_FILEENTRY_H__
#include "Entry.h"
#include <NufxLib.h>
namespace NuFX {
class FileEntry : public Entry
{
public:
private:
NuRecordIdx _recordID;
unsigned _flags; // threads
size_t _size; // data size
};
}
#endif

14
NuFX/VolumeEntry.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "VolumeEntry.h"
using namespace NuFX;
void VolumeEntry::addEntry(EntryPointer e)
{
if (!e) return;
e->setVolume(pointer());
_inodeIndex->push_back(e);
e->_inode = _inodeIndex->length() + 100 - 1;
}

35
NuFX/VolumeEntry.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __NUFX_VOLUMEENTRY_H__
#define __NUFX_VOLUMEENTRY_H__
#include "DirectoryEntry.h"
#include <Common/unordered_map.h>
namespace NuFX {
class VolumeEntry : public DirectoryEntry
{
public:
void addEntry(EntryPointer);
private:
VolumeEntryPointer pointer() const
{
return STATIC_POINTER_CAST(VolumeEntryPointer, shared_from_this());
}
void parse();
NuArchive *_archive;
//unsigned _inodeGenerator;
std::vector<WeakPointer> _inodeIndex;
};
}
#endif

13
POSIX/Exception.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "Exception.h"
#include <cstdio>
#include <cstring>
namespace POSIX {
const char *Exception::errorString()
{
return strerror(error());
}
}

34
POSIX/Exception.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef __POSIX_EXCEPTION_H__
#define __POSIX_EXCEPTION_H__
#include <Common/Exception.h>
namespace POSIX {
class Exception : public ::Exception {
public:
Exception(const char *cp, int error);
Exception(const std::string& string, int error);
virtual const char *errorString();
private:
typedef ::Exception super;
};
inline Exception::Exception(const char *cp, int error) :
super(cp, error)
{
}
inline Exception::Exception(const std::string& string, int error) :
super(string, error)
{
}
}
#endif

View File

@ -1,13 +1,11 @@
#include <Pascal/File.h>
#include <Pascal/Entry.h>
#include <ProfUSE/auto.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Device/BlockDevice.h>
#include <Device/BlockCache.h>
#include <algorithm>
@ -66,7 +64,7 @@ Entry::Entry()
_lastBlock = 0;
_fileKind = 0;
_inode = 0;
_parent = NULL;
_address = NULL;
}
Entry::Entry(void *vp)

89
Pascal/Entry.h Normal file
View File

@ -0,0 +1,89 @@
#ifndef __PASCAL_ENTRY_H__
#define __PASCAL_ENTRY_H__
#include <Common/smart_pointers.h>
namespace Device {
class BlockDevice;
class BlockCache;
}
namespace LittleEndian {
class IOBuffer;
}
namespace Pascal {
enum {
kUntypedFile,
kBadBlockFile,
kCodeFile,
kTextFile,
kInfoFile,
kDataFile,
kGrafFile,
kFotoFile,
kSecureDir
};
class FileEntry;
class VolumeEntry;
typedef SHARED_PTR(FileEntry) FileEntryPointer;
typedef SHARED_PTR(VolumeEntry) VolumeEntryPointer;
typedef WEAK_PTR(FileEntry) FileEntryWeakPointer;
typedef WEAK_PTR(VolumeEntry) VolumeEntryWeakPointer;
class Entry : public ENABLE_SHARED_FROM_THIS(Entry) {
public:
virtual ~Entry();
unsigned blocks() const { return _lastBlock - _firstBlock; }
unsigned firstBlock() const { return _firstBlock; }
unsigned lastBlock() const { return _lastBlock; }
unsigned fileKind() const { return _fileKind; }
unsigned inode() const { return _inode; }
void setInode(unsigned inode) { _inode = inode; }
VolumeEntryWeakPointer parent() { return _parent; }
protected:
static unsigned ValidName(const char *name, unsigned maxSize);
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
Entry();
Entry(void *);
void init(void *);
unsigned _firstBlock;
unsigned _lastBlock;
unsigned _fileKind;
unsigned _inode;
private:
friend class VolumeEntry;
VolumeEntryWeakPointer _parent;
unsigned _address;
};
}
#endif

View File

@ -1,187 +0,0 @@
#ifndef __FILE_H__
#define __FILE_H__
#include <Pascal/Date.h>
#include <vector>
namespace Device {
class BlockDevice;
class AbstractBlockCache;
}
namespace LittleEndian {
class IOBuffer;
}
namespace Pascal {
enum {
kUntypedFile,
kBadBlockFile,
kCodeFile,
kTextFile,
kInfoFile,
kDataFile,
kGrafFile,
kFotoFile,
kSecureDir
};
class FileEntry;
class VolumeEntry;
class Entry {
public:
virtual ~Entry();
unsigned blocks() const { return _lastBlock - _firstBlock; }
unsigned firstBlock() const { return _firstBlock; }
unsigned lastBlock() const { return _lastBlock; }
unsigned fileKind() const { return _fileKind; }
unsigned inode() const { return _inode; }
void setInode(unsigned inode) { _inode = inode; }
VolumeEntry *parent() { return _parent; }
protected:
static unsigned ValidName(const char *name, unsigned maxSize);
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
Entry();
Entry(void *);
void init(void *);
unsigned _firstBlock;
unsigned _lastBlock;
unsigned _fileKind;
unsigned _inode;
private:
friend class VolumeEntry;
VolumeEntry *_parent;
};
class VolumeEntry : public Entry {
public:
// create new
VolumeEntry(const char *name, Device::BlockDevice *);
// open existing
VolumeEntry(Device::BlockDevice *);
virtual ~VolumeEntry();
const char *name() const { return _fileName; }
unsigned fileCount() const { return _fileCount; }
unsigned volumeBlocks() const { return _lastVolumeBlock; }
Pascal::Date lastBoot() const { return _lastBoot; }
FileEntry *fileAtIndex(unsigned i) const;
void addChild(FileEntry *child, unsigned blocks);
void *loadBlock(unsigned block);
void unloadBlock(unsigned block, bool dirty = false);
void readBlock(unsigned block, void *);
void writeBlock(unsigned block, void *);
unsigned static ValidName(const char *);
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
VolumeEntry();
void init(void *);
unsigned _fileNameLength;
char _fileName[8];
unsigned _lastVolumeBlock;
unsigned _fileCount;
unsigned _accessTime;
Pascal::Date _lastBoot;
std::vector<FileEntry *> _files;
unsigned _inodeGenerator;
Device::BlockDevice *_device;
Device::AbstractBlockCache *_cache;
};
class FileEntry : public Entry {
public:
FileEntry(const char *name, unsigned fileKind);
FileEntry(void *vp);
virtual ~FileEntry();
unsigned fileSize();
unsigned lastByte() const { return _lastByte; }
int read(uint8_t *buffer, unsigned size, unsigned offset);
int write(uint8_t *buffer, unsigned size, unsigned offset);
const char *name() const { return _fileName; }
Date modification() const { return _modification; }
unsigned static ValidName(const char *);
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
unsigned _status;
unsigned _fileNameLength;
char _fileName[16];
unsigned _lastByte;
Date _modification;
// non-text files
unsigned dataFileSize();
int dataRead(uint8_t *buffer, unsigned size, unsigned offset);
// for text files.
void textInit();
unsigned textFileSize();
int textRead(uint8_t *buffer, unsigned size, unsigned offset);
unsigned textReadPage(unsigned block, uint8_t *in);
unsigned textDecodePage(unsigned block, uint8_t *out);
std::vector<unsigned> *_pageSize;
unsigned _fileSize;
};
}
#endif

View File

@ -3,17 +3,19 @@
#include <cstring>
#include <cctype>
#include <memory>
#include <cerrno>
#include <Pascal/File.h>
#include <Pascal/Pascal.h>
#include <Pascal/TextWriter.h>
#include <ProfUSE/auto.h>
#include <ProFUSE/Exception.h>
#include <Common/auto.h>
#include <Common/Exception.h>
#include <ProDOS/Exception.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <Device/BlockDevice.h>
#include <Device/BlockCache.h>
@ -21,14 +23,38 @@ using namespace LittleEndian;
using namespace Pascal;
#pragma mark -
#pragma mark FileEntry
enum {
kDLE = 16
};
/*
* _lastByte is in the range 1..512 and indicates how many bytes in the last
* block are in use.
* _lastBlock is the block *after* the actual last block.
* _maxFileSize is the maximum file size the file can grow to.
*
*/
unsigned FileEntry::ValidName(const char *cp)
{
return Entry::ValidName(cp, 15);
}
FileEntryPointer FileEntry::Open(void *vp)
{
//return FileEntryPointer(new FileEntry(vp));
return MAKE_SHARED(FileEntry, vp);
}
FileEntryPointer FileEntry::Create(const char *name, unsigned fileKind)
{
//return FileEntryPointer(new FileEntry(name, fileKind));
return MAKE_SHARED(FileEntry, name, fileKind);
}
FileEntry::FileEntry(void *vp) :
Entry(vp)
{
@ -41,6 +67,7 @@ FileEntry::FileEntry(void *vp) :
_fileSize = 0;
_pageSize = NULL;
_maxFileSize = 0;
}
FileEntry::FileEntry(const char *name, unsigned fileKind)
@ -51,7 +78,7 @@ FileEntry::FileEntry(const char *name, unsigned fileKind)
unsigned length = ValidName(name);
if (!length)
throw ProFUSE::Exception(__METHOD__ ": Invalid file name.");
throw ::Exception(__METHOD__ ": Invalid file name.");
_fileKind = fileKind;
_status = 0;
@ -66,6 +93,7 @@ FileEntry::FileEntry(const char *name, unsigned fileKind)
_fileSize = 0;
_pageSize = NULL;
_maxFileSize = 0;
}
FileEntry::~FileEntry()
@ -74,6 +102,42 @@ FileEntry::~FileEntry()
}
void FileEntry::setFileKind(unsigned kind)
{
_fileKind = kind;
if (_pageSize)
{
delete _pageSize;
_fileSize = 0;
_pageSize = NULL;
}
VolumeEntryPointer v = parent().lock();
// throw if expired?
if (v) v->writeEntry(this);
}
void FileEntry::setName(const char *name)
{
#undef __METHOD__
#define __METHOD__ "FileEntry::setName"
unsigned length = ValidName(name);
if (!length)
throw ProDOS::Exception(__METHOD__ ": Invalid file name.", ProDOS::badPathSyntax);
_fileNameLength = length;
for (unsigned i = 0; i < length; ++i)
_fileName[i] = std::toupper(name[i]);
// parent's responsibility.
//parent()->writeEntry(this);
}
unsigned FileEntry::fileSize()
{
switch(fileKind())
@ -117,6 +181,258 @@ int FileEntry::read(uint8_t *buffer, unsigned size, unsigned offset)
}
int FileEntry::truncate(unsigned newSize)
{
#undef __METHOD__
#define __METHOD__ "FileEntry::truncate"
unsigned currentSize = fileSize();
if (currentSize == newSize) return 0;
if (fileKind() == kTextFile)
{
if (newSize)
{
errno = EINVAL;
return -1;
}
newSize = 2; // text files have a 2-page scratch buffer for the editor.
if (_pageSize)
{
_pageSize->clear();
_fileSize = 0;
}
}
if (truncateCommon(newSize) != 0)
return -1;
_modification = Date::Today();
VolumeEntryPointer v = parent().lock();
if (v) v->writeEntry(this);
return 0;
}
/*
* truncateCommon -- common truncation code.
* updates _lastByte and _lastBlock but does
* not update _modification or commit to disk.
*/
int FileEntry::truncateCommon(unsigned newSize)
{
#undef __METHOD__
#define __METHOD__ "FileEntry::truncateCommon"
unsigned currentSize = fileSize();
VolumeEntryPointer v = parent().lock();
if (newSize == currentSize) return 0;
if (newSize > currentSize)
{
if (newSize > _maxFileSize)
{
errno = ENOSPC;
return -1;
}
unsigned remainder = newSize - currentSize;
unsigned block = _lastBlock - 1;
if (_lastByte != 512)
{
// last page not full
unsigned count = std::min(512 - _lastByte, remainder);
if (v)
{
uint8_t *address = (uint8_t *)v->loadBlock(block);
std::memset(address + _lastByte, 0, count);
v->unloadBlock(block, true);
}
remainder -= count;
}
block++;
while (remainder)
{
unsigned count = std::min(512u, remainder);
if (v)
{
uint8_t *address = (uint8_t *)v->loadBlock(block);
std::memset(address, 0, count);
v->unloadBlock(block, true);
}
remainder -= count;
block++;
}
}
setFileSize(newSize);
return 0;
}
int FileEntry::write(TextWriter &text)
{
unsigned blocks = text.blocks();
VolumeEntryPointer v = parent().lock();
if (!v)
{
errno = EROFS;
return -1;
}
if (v->readOnly())
{
errno = EROFS;
return -1;
}
if (fileKind() != kTextFile)
{
errno = EINVAL;
return -1;
}
if (blocks * 512 > _maxFileSize)
{
errno = ENOSPC;
return -1;
}
for (unsigned i = 0; i < blocks; ++i)
{
void *buffer = text.data(i);
v->writeBlock(_firstBlock + i, buffer);
}
_modification = Date::Today();
setFileSize(blocks * 512);
v->writeEntry(this);
v->sync();
return blocks * 512;
}
int FileEntry::write(const uint8_t *buffer, unsigned size, unsigned offset)
{
#undef __METHOD__
#define __METHOD__ "FileEntry::write"
VolumeEntryPointer v = parent().lock();
if (!v)
{
errno = EROFS;
return -1;
}
if (v->readOnly())
{
errno = EROFS;
return -1;
}
if (fileKind() == kTextFile)
{
errno = EINVAL;
return -1;
}
unsigned currentSize = fileSize();
unsigned newSize = std::max(offset + size, currentSize);
if (newSize > _maxFileSize)
{
errno = ENOSPC;
return -1;
}
if (offset > currentSize)
{
if (truncateCommon(offset) != 0) return -1;
}
// now write the data...
unsigned block = firstBlock() + offset / 512;
unsigned start = offset % 512;
unsigned remainder = size;
if (start)
{
unsigned count = std::min(512 - start, remainder);
uint8_t *address = (uint8_t *)v->loadBlock(block);
std::memcpy(address + start, buffer, count);
v->unloadBlock(block, true);
remainder -= count;
buffer += count;
block++;
}
while (remainder)
{
uint8_t *address = (uint8_t *)v->loadBlock(block);
unsigned count = std::min(512u, size);
std::memcpy(address, buffer, count);
v->unloadBlock(block, true);
remainder -= count;
buffer += count;
block++;
}
if (newSize > currentSize) setFileSize(newSize);
_modification = Date::Today();
v->writeEntry(this);
return size;
}
/*
* private
* set the file size. Does not check if > _maxFileSize.
*
*/
void FileEntry::setFileSize(unsigned size)
{
if (size == 0)
{
// TODO -- verify how 0 byte files are handled.
_lastBlock = _firstBlock + 1;
_lastByte = 0;
return;
}
_lastBlock = 1 + _firstBlock + size / 512;
_lastByte = size % 512;
if (_lastByte == 0) _lastByte = 512;
}
unsigned FileEntry::dataFileSize()
{
return blocks() * 512 - 512 + _lastByte;
@ -139,6 +455,13 @@ int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
unsigned count = 0;
unsigned block = 0;
VolumeEntryPointer v = parent().lock();
if (!v)
{
errno = EROFS;
return 0;
}
block = _firstBlock + (offset / 512);
@ -153,7 +476,7 @@ int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
{
unsigned bytes = std::min(offset % 512, size);
parent()->readBlock(block++, tmp);
v->readBlock(block++, tmp);
std::memcpy(buffer, tmp + 512 - bytes, bytes);
@ -168,7 +491,7 @@ int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
while (size >= 512)
{
parent()->readBlock(block++, buffer);
v->readBlock(block++, buffer);
buffer += 512;
count += 512;
@ -180,7 +503,7 @@ int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
*/
if (size)
{
parent()->readBlock(block, tmp);
v->readBlock(block, tmp);
std::memcpy(buffer, tmp, size);
count += size;
@ -201,7 +524,7 @@ int FileEntry::textRead(uint8_t *buffer, unsigned size, unsigned offset)
unsigned l;
unsigned count = 0;
ProFUSE::auto_array<uint8_t> tmp;
::auto_array<uint8_t> tmp;
unsigned tmpSize = 0;
if (!_pageSize) textInit();
@ -221,7 +544,7 @@ int FileEntry::textRead(uint8_t *buffer, unsigned size, unsigned offset)
to += pageSize;
}
// first 2 pages are spare, for editor use, not actually text.
block = _firstBlock + 2 + (page * 2);
@ -290,7 +613,7 @@ unsigned FileEntry::textDecodePage(unsigned block, uint8_t *out)
dle = false;
continue;
}
if (c == 16) { dle = true; continue; }
if (c == kDLE) { dle = true; continue; }
//if (c & 0x80) continue; // ascii only.
@ -309,13 +632,20 @@ unsigned FileEntry::textReadPage(unsigned block, uint8_t *in)
// reads up to 2 blocks.
// assumes block within _startBlock ... _lastBlock - 1
parent()->readBlock(block, in);
VolumeEntryPointer v = parent().lock();
if (!v)
{
errno = EROFS;
return 0;
}
v->readBlock(block, in);
if (block + 1 == _lastBlock)
{
return _lastByte;
}
parent()->readBlock(block + 1, in + 512);
v->readBlock(block + 1, in + 512);
if (block +2 == _lastBlock)
{
return 512 + _lastByte;
@ -336,8 +666,69 @@ void FileEntry::textInit()
for (unsigned block = _firstBlock + 2; block < _lastBlock; block += 2)
{
unsigned size = textDecodePage(block, NULL);
printf("%u: %u\n", block, size);
//printf("%u: %u\n", block, size);
_fileSize += size;
_pageSize->push_back(size);
}
}
/*
* compress white space into a dle.
* returns true if altered.
* nb -- only leading white space is compressed.
*/
bool FileEntry::Compress(std::string& text)
{
std::string out;
size_t pos;
size_t count;
if (text.length() < 3) return false;
if (text[0] != ' ') return false;
pos = text.find_first_not_of(' ');
if (pos == std::string::npos)
count = text.length();
else count = pos;
if (count < 3) return false;
count = std::min((int)count, 255 - 32);
out.push_back(kDLE);
out.push_back(32 + count);
out.append(text.begin() + count, text.end());
text.swap(out);
return true;
}
/*
* dle will only occur at start.
*
*/
bool FileEntry::Uncompress(std::string& text)
{
std::string out;
unsigned c;
if (text.length() < 2) return false;
if (text[0] != kDLE) return false;
c = text[1];
if (c < 32) c = 32;
out.append(c - 32, ' ');
out.append(text.begin() + 2, text.end());
text.swap(out);
return true;
}

111
Pascal/FileEntry.h Normal file
View File

@ -0,0 +1,111 @@
#ifndef __PASCAL_FILEENTRY_H__
#define __PASCAL_FILEENTRY_H__
#include <Pascal/Entry.h>
#include <Pascal/Date.h>
#include <vector>
#include <string>
namespace Pascal {
class TextWriter;
class FileEntry : public Entry {
public:
static unsigned ValidName(const char *);
static bool Compress(std::string& text);
static bool Uncompress(std::string& text);
static FileEntryPointer Create(const char *name, unsigned fileKind);
static FileEntryPointer Open(void *vp);
virtual ~FileEntry();
unsigned fileSize();
unsigned lastByte() const { return _lastByte; }
const char *name() const { return _fileName; }
Date modification() const { return _modification; }
void setFileKind(unsigned kind);
int read(uint8_t *buffer, unsigned size, unsigned offset);
int write(const uint8_t *buffer, unsigned size, unsigned offset);
int write(TextWriter& text);
int truncate(unsigned newSize);
FileEntry(const char *name, unsigned fileKind);
FileEntry(void *vp);
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
friend class VolumeEntry;
FileEntryPointer thisPointer()
{
return STATIC_POINTER_CAST(FileEntry, shared_from_this());
}
void setName(const char *name);
int truncateCommon(unsigned newSize);
void setFileSize(unsigned size);
unsigned _status;
unsigned _fileNameLength;
char _fileName[16];
unsigned _lastByte;
Date _modification;
unsigned _maxFileSize; // maximum file size.
// non-text files
unsigned dataFileSize();
int dataRead(uint8_t *buffer, unsigned size, unsigned offset);
// for text files.
void textInit();
unsigned textFileSize();
int textRead(uint8_t *buffer, unsigned size, unsigned offset);
unsigned textReadPage(unsigned block, uint8_t *in);
unsigned textDecodePage(unsigned block, uint8_t *out);
std::vector<unsigned> *_pageSize;
unsigned _fileSize;
};
}
#endif

9
Pascal/Pascal.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __PASCAL_FILE_H__
#define __PASCAL_FILE_H__
#include <Pascal/Date.h>
#include <Pascal/Entry.h>
#include <Pascal/FileEntry.h>
#include <Pascal/VolumeEntry.h>
#endif

104
Pascal/TextWriter.cpp Normal file
View File

@ -0,0 +1,104 @@
#include <Pascal/TextWriter.h>
#include <Pascal/FileEntry.h>
#include <Common/Exception.h>
#include <string>
#include <cstring>
using namespace Pascal;
TextWriter::TextWriter()
{
_offset = 0;
_current = new uint8_t[1024];
_blocks.push_back(new uint8_t[1024]); // 1024k for editor scratch data.
std::memset(_blocks.back(), 0, 1024);
std::memset(_current, 0, 1024);
}
TextWriter::~TextWriter()
{
std::vector<uint8_t *>::iterator iter;
if (_current) delete[] _current;
for (iter = _blocks.begin(); iter != _blocks.end(); ++iter)
{
delete[] *iter;
}
}
unsigned TextWriter::blocks() const
{
if (_offset == 0) return _blocks.size() * 2;
if (_offset <= 512) return _blocks.size() * 2 + 1;
return _blocks.size() * 2 + 2;
}
void *TextWriter::data(unsigned block) const
{
unsigned offset = (block & 0x01) * 512;
unsigned halfBlock = block >> 1;
if (halfBlock < _blocks.size())
{
return _blocks[halfBlock] + offset;
}
if (halfBlock == _blocks.size())
{
if (offset > _offset) return NULL;
return _current + offset;
}
return NULL;
}
void TextWriter::writeLine(const char *line)
{
writeLine(line, std::strlen(line));
}
void TextWriter::writeLine(const char *line, unsigned length)
{
#undef __METHOD__
#define __METHOD__ "TextWriter::writeLine"
if (line == NULL) line = "";
std::string text(line, length);
if (length)
{
char c = text[length - 1];
if (c == 0x0a) text[length - 1] = 0x0d;
else if (c != 0x0d) text.push_back(0x0d);
FileEntry::Compress(text);
}
else
{
text.push_back(0x0d);
}
length = text.length();
if (length > 1024)
{
throw ::Exception(__METHOD__ ": String is too long.");
}
if (_offset + length > 1024)
{
_blocks.push_back(_current);
_offset = 0;
_current = new uint8_t[1024];
std::memset(_current, 0, 1024);
}
std::memcpy(_current + _offset, text.data(), length);
_offset += length;
}

37
Pascal/TextWriter.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef __PASCAL_TEXTWRITER_H__
#define __PASCAL_TEXTWRITER_H__
#include <vector>
#include <stdint.h>
namespace Pascal {
class TextWriter {
public:
TextWriter();
~TextWriter();
unsigned blocks() const;
void *data(unsigned block) const;
void writeLine(const char *);
void writeLine(const char *, unsigned length);
private:
std::vector<uint8_t *> _blocks;
unsigned _offset;
uint8_t *_current;
};
}
#endif

File diff suppressed because it is too large Load Diff

117
Pascal/VolumeEntry.h Normal file
View File

@ -0,0 +1,117 @@
#ifndef __PASCAL_VOLUMEENTRY_H__
#define __PASCAL_VOLUMEENTRY_H__
#include <Pascal/Entry.h>
#include <vector>
#include <Device/BlockDevice.h>
namespace Pascal {
class VolumeEntry : public Entry {
public:
static unsigned ValidName(const char *);
static VolumeEntryPointer Open(Device::BlockDevicePointer);
static VolumeEntryPointer Create(Device::BlockDevicePointer, const char *name);
//
virtual ~VolumeEntry();
const char *name() const { return _fileName; }
unsigned fileCount() const { return _fileCount; }
unsigned volumeBlocks() const { return _lastVolumeBlock; }
Pascal::Date lastBoot() const { return _lastBoot; }
unsigned freeBlocks(bool krunched = false) const;
unsigned maxContiguousBlocks() const;
bool canKrunch() const;
FileEntryPointer fileAtIndex(unsigned i) const;
FileEntryPointer fileByName(const char *name) const;
void *loadBlock(unsigned block);
void unloadBlock(unsigned block, bool dirty = false);
void readBlock(unsigned block, void *);
void writeBlock(unsigned block, void *);
void sync();
bool readOnly() { return _device->readOnly(); }
int unlink(const char *name);
int rename(const char *oldName, const char *newName);
int copy(const char *oldName, const char *newName);
FileEntryPointer create(const char *name, unsigned blocks);
int krunch();
VolumeEntry(Device::BlockDevicePointer, const char *name);
VolumeEntry(Device::BlockDevicePointer);
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
friend class FileEntry;
VolumeEntry();
VolumeEntryPointer thisPointer()
{
return STATIC_POINTER_CAST(VolumeEntry, shared_from_this());
}
void init(void *);
void setParents();
uint8_t *readDirectoryHeader();
void writeDirectoryHeader(void *);
uint8_t *readBlocks(unsigned startingBlock, unsigned count);
void writeBlocks(void *buffer, unsigned startingBlock, unsigned count);
void writeEntry(FileEntry *e);
void writeEntry();
void calcMaxFileSize();
unsigned _fileNameLength;
char _fileName[8];
unsigned _lastVolumeBlock;
unsigned _fileCount;
unsigned _accessTime;
Pascal::Date _lastBoot;
std::vector<FileEntryPointer> _files;
unsigned _inodeGenerator;
Device::BlockDevicePointer _device;
Device::BlockCachePointer _cache;
};
}
#endif

View File

@ -1,11 +1,10 @@
#include <cstring>
#include "Bitmap.h"
#include "BlockDevice.h"
#include "auto.h"
#include <ProDOS/Bitmap.h>
#include <Device/BlockDevice.h>
#include <Cache/BlockCache.h>
using namespace ProFUSE;
using namespace ProDOS;
// returns # of 1-bits set (0-8)
inline static unsigned popCount(uint8_t x)
@ -34,26 +33,25 @@ Bitmap::Bitmap(unsigned blocks)
_freeIndex = 0;
unsigned bitmapSize = _bitmapBlocks * 512;
unsigned blockSize = blocks / 8;
auto_array<uint8_t> bitmap(new uint8_t[bitmapSize]);
// mark overflow in use, everything else free.
std::memset(bitmap, 0xff, blocks / 8);
std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize);
_bitmap.reserve(bitmapSize);
// edge case
unsigned tmp = blocks & 0x07;
// mark blocks as free..
_bitmap.resize(blocks / 8, 0xff);
bitmap[blocks / 8] = ~(0xff >> tmp);
// edge case...
_bitmap = bitmap.release();
if (blocks & 0x0f)
{
_bitmap.push_back( ~(0xff >> (blocks & 0x0f)) );
}
// mark any trailing blocks as in use.
_bitmap.resize(bitmapSize, 0x00);
}
Bitmap::Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks)
Bitmap::Bitmap(Device::BlockCache *cache, unsigned keyPointer, unsigned blocks)
{
_blocks = blocks;
_freeBlocks = 0;
@ -64,47 +62,68 @@ Bitmap::Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks)
unsigned bitmapSize = _bitmapBlocks * 512;
unsigned blockSize = blocks / 8;
auto_array<uint8_t> bitmap(new uint8_t[bitmapSize]);
_bitmap.reserve(bitmapSize);
// load the full block(s).
for (unsigned i = 0; i < blockSize; ++i)
{
device->read(keyPointer + i, bitmap + 512 * i);
uint8_t *buffer = (uint8_t *)cache->acquire(keyPointer);
_bitmap.insert(_bitmap.end(), buffer, buffer + 512);
cache->release(keyPointer);
keyPointer++;
}
// make sure all trailing bits are marked in use.
// edge case
unsigned tmp = blocks & 0x07;
// and any remaining partial block.
bitmap[blocks / 8] &= ~(0xff >> tmp);
std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize);
// set _freeBlocks and _freeIndex;
for (unsigned i = 0; i < (blocks + 7) / 8; ++i)
if (blocks & 4095)
{
_freeBlocks += popCount(bitmap[i]);
}
if (_freeBlocks)
{
for (unsigned i = 0; i < (blocks + 7) / 8; ++i)
uint8_t *buffer = (uint8_t *)cache->acquire(keyPointer);
unsigned bits = blocks & 4095;
unsigned bytes = bits / 8;
//for (unsigned i = 0; i < bits / 8; ++i) _bitmap.push_back(buffer[i]);
_bitmap.insert(_bitmap.end(), buffer, buffer + bytes);
// partial...
if (blocks & 0x0f)
{
if (bitmap[i])
{
_freeIndex = i;
break;
}
uint8_t tmp = buffer[bytes];
tmp &= ~(0xff >> (blocks & 0x0f));
_bitmap.push_back(tmp);
}
}
// remainder set to in use.
_bitmap.resize(bitmapSize, 0x00);
cache->release(keyPointer);
keyPointer++;
}
_bitmap = bitmap.release();
// now set _freeBlocks and _freeIndex;
std::vector<uint8_t>::iterator iter;
_freeIndex = -1;
for (iter = _bitmap.begin(); iter != _bitmap.end(); ++iter)
{
_freeBlocks += popCount(*iter);
if (_freeIndex == -1 && *iter)
_freeIndex = std::distance(_bitmap.begin(), iter);
}
}
Bitmap::~Bitmap()
{
if (_bitmap) delete []_bitmap;
}

View File

@ -2,20 +2,25 @@
#define __BITMAP_H__
#include <stdint.h>
#include <vector>
namespace Device
{
class BlockDevice;
class BlockCache;
}
namespace ProFUSE {
class BlockDevice;
namespace ProDOS {
class Bitmap {
public:
Bitmap(unsigned blocks);
Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks);
//todo -- constructor by loading fro, block device...
Bitmap(Device::BlockCache *cache, unsigned keyPointer, unsigned blocks);
~Bitmap();
int allocBlock();
@ -28,7 +33,7 @@ public:
unsigned blocks() const { return _blocks; }
unsigned bitmapBlocks() const { return _bitmapBlocks; }
unsigned bitmapSize() const { return _bitmapBlocks * 512; }
const void *bitmap() const { return _bitmap; }
const void *bitmap() const { return &_bitmap[0]; }
private:
@ -38,7 +43,7 @@ private:
unsigned _blocks;
unsigned _bitmapBlocks;
uint8_t *_bitmap;
std::vector<uint8_t> _bitmap;
};

486
ProDOS/Disk.cpp Normal file
View File

@ -0,0 +1,486 @@
/*
* Disk.cpp
* ProFUSE
*
* Created by Kelvin Sherlock on 12/18/08.
*
*/
#include "Disk.h"
#include "common.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <vector>
#include <Endian/Endian.h>
struct ucmp
{
bool operator()(unsigned a, unsigned b) const
{
return a < b;
}
};
using std::set;
using std::vector;
using namespace LittleEndian;
typedef set<unsigned, ucmp> uset;
Disk::Disk()
{
_blocks = 0;
}
Disk::~Disk()
{
}
Disk::Disk(Device::BlockDevicePointer device) :
_device(device)
{
_blocks = _device->blocks();
}
DiskPointer Disk::OpenFile(Device::BlockDevicePointer device)
{
DiskPointer disk(new Disk(device));
return disk;
}
// load the mini entry into the regular entry.
int Disk::Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee)
{
uint8_t buffer[BLOCK_SIZE];
int ok;
if (fork > 1) return -P8_INVALID_FORK;
if (f.storage_type != EXTENDED_FILE)
{
return fork == 0 ? 0 : -P8_INVALID_FORK;
}
ok = Read(f.key_pointer, buffer);
if (ok < 0) return ok;
ExtendedEntry e;
e.Load(buffer);
if (fork == 0)
{
f.storage_type = e.dataFork.storage_type;
f.key_pointer = e.dataFork.key_block;
f.eof = e.dataFork.eof;
f.blocks_used = e.dataFork.blocks_used;
}
else
{
f.storage_type = e.resourceFork.storage_type;
f.key_pointer = e.resourceFork.key_block;
f.eof = e.resourceFork.eof;
f.blocks_used = e.resourceFork.blocks_used;
}
if (ee) *ee = e;
return 0;
}
int Disk::Read(unsigned block, void *buffer)
{
if (block > _blocks) return -P8_INVALID_BLOCK;
_device->read(block, buffer);
return 1;
}
void *Disk::ReadFile(const FileEntry &f, unsigned fork, uint32_t *size, int *error)
{
#define SET_ERROR(x) if (error) *error = (x)
#define SET_SIZE(x) if (size) *size = (x)
SET_ERROR(0);
SET_SIZE(0);
if (fork != P8_DATA_FORK && fork != P8_RESOURCE_FORK)
{
SET_ERROR(-P8_INVALID_FORK);
return NULL;
}
uint8_t buffer[BLOCK_SIZE];
int ok;
uint32_t eof;
uint32_t alloc;
unsigned blocks;
unsigned storage_type;
unsigned key_block;
switch(f.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
if (fork != P8_DATA_FORK)
{
SET_ERROR(1);
return NULL;
}
storage_type = f.storage_type;
eof = f.eof;
key_block = f.key_pointer;
break;
case EXTENDED_FILE:
{
ok = Read(f.key_pointer, buffer);
if (ok < 0)
{
SET_ERROR(ok);
return NULL;
}
ExtendedEntry entry;
entry.Load(buffer);
if (fork == P8_DATA_FORK)
{
storage_type = entry.dataFork.storage_type;
eof = entry.dataFork.eof;
key_block = entry.dataFork.key_block;
}
else
{
storage_type = entry.resourceFork.storage_type;
eof = entry.resourceFork.eof;
key_block = entry.resourceFork.key_block;
}
}
break;
default:
SET_ERROR(-P8_INVALID_STORAGE_TYPE);
return NULL;
}
if (eof == 0)
{
SET_ERROR(1);
return NULL;
}
blocks = (eof + BLOCK_SIZE - 1) >> 9;
alloc = (eof + BLOCK_SIZE - 1) & (~BLOCK_SIZE);
uint8_t* data = new uint8_t[alloc];
switch (storage_type)
{
case SEEDLING_FILE:
ok = Read(key_block, data);
break;
case SAPLING_FILE:
ok = ReadIndex(f.key_pointer, buffer, 1, 0, blocks);
break;
case TREE_FILE:
ok = ReadIndex(f.key_pointer, buffer, 2, 0, blocks);
break;
default:
ok = false;
}
if (ok < 0)
{
SET_ERROR(ok);
delete[] data;
return NULL;
}
bzero(data + eof, alloc - eof);
SET_SIZE(eof);
return data;
}
int Disk::ReadFile(const FileEntry &f, void *buffer)
{
int blocks = (f.eof + BLOCK_SIZE - 1) >> 9;
int ok;
switch(f.storage_type)
{
case TREE_FILE:
ok = ReadIndex(f.key_pointer, buffer, 2, 0, blocks);
break;
case SAPLING_FILE:
ok = ReadIndex(f.key_pointer, buffer, 1, 0, blocks);
break;
case SEEDLING_FILE:
ok = Read(f.key_pointer, buffer);
break;
default:
return -P8_INVALID_STORAGE_TYPE;
}
if (ok >= 0)
{
bzero((uint8_t *)buffer + f.eof, (blocks << 9) - f.eof);
}
return ok;
}
int Disk::ReadIndex(unsigned block, void *buffer, unsigned level, off_t offset, unsigned blocks)
{
if (level == 0)
{
// data level
if (block == 0) // sparse file
{
bzero(buffer, BLOCK_SIZE);
return 1;
}
return Read(block, buffer);
}
unsigned blockCount;
unsigned readSize;
unsigned first;
//unsigned last;
switch(level)
{
case 1:
first = (offset >> 9) & 0xff;
blockCount = 1;
readSize = BLOCK_SIZE;
offset = 0;
break;
case 2:
first = (offset >> 17) & 0xff;
blockCount = 256;
readSize = BLOCK_SIZE << 8;
offset &= 0x1ffff;
break;
default:
return -P8_INTERNAL_ERROR;
}
int ok;
uint8_t key[BLOCK_SIZE];
if (block) // not sparse.
{
ok = Read(block, key);
if (ok < 0 ) return ok;
}
else
{
// sparse -- zero it out so code below works w/o special cases.
bzero(key, BLOCK_SIZE);
}
for (unsigned i = first; blocks; i++)
{
// block pointers are split up since 8-bit indexing is limited to 256.
unsigned newBlock = (key[i]) | (key[256 + i] << 8);
unsigned b = std::min(blocks, blockCount);
ok = ReadIndex(newBlock, buffer, level - 1, offset, b);
if (ok < 0) return ok;
offset = 0;
buffer = ((char *)buffer) + readSize;
blocks -= b;
}
return blocks;
}
int Disk::ReadVolume(VolumeEntry *volume, std::vector<FileEntry> *files)
{
if (files) files->resize(0);
uint8_t buffer[BLOCK_SIZE];
int ok;
unsigned prev;
unsigned next;
uset blocks;
unsigned block = 2;
blocks.insert(block);
ok = Read(block, buffer);
if (ok < 0) return ok;
prev = Read16(&buffer[0x00]);
next = Read16(&buffer[0x02]);
VolumeEntry v;
v.Load(buffer + 0x04);
if (v.storage_type != VOLUME_HEADER) return -P8_INVALID_STORAGE_TYPE;
if (volume) *volume = v;
if (!files) return 1;
if (v.file_count)
{
files->reserve(v.file_count);
//files->resize(v.file_count);
//unsigned count = 0;
unsigned index = 1; // skip the header.
for(;;)
{
//
if ( (buffer[0x04 + v.entry_length * index] >> 4) != DELETED_FILE)
{
unsigned offset = v.entry_length * index + 0x4;
FileEntry f;
f.Load(buffer + offset);
f.address = (block << 9) + offset;
files->push_back(f);
//if (++count == v.file_count) break;
}
index++;
if (index >= v.entries_per_block)
{
if (!next) break; // all done!
if (blocks.insert(next).second == false)
{
return -P8_CYCLICAL_BLOCK;
}
ok = Read(next, buffer);
if (ok < 0) return ok;
block = next;
prev = Read16(&buffer[0x00]);
next = Read16(&buffer[0x02]);
index = 0;
}
}
}
return 1;
}
int Disk::ReadDirectory(unsigned block, SubdirEntry *dir, std::vector<FileEntry> *files)
{
if (files) files->resize(0);
uint8_t buffer[BLOCK_SIZE];
int ok;
unsigned prev;
unsigned next;
// keep a list of blocks to prevent cyclical problems.
uset blocks;
blocks.insert(block);
ok = Read(block, buffer);
if (ok < 0) return ok;
prev = Read16(&buffer[0x00]);
next = Read16(&buffer[0x02]);
SubdirEntry v;
v.Load(buffer + 0x04);
if (v.storage_type != SUBDIR_HEADER) return -P8_INVALID_STORAGE_TYPE;
if (dir) *dir = v;
if (!files) return 1;
if (v.file_count)
{
files->reserve(v.file_count);
//files->resize(v.file_count);
//unsigned count = 0;
unsigned index = 1; // skip the header.
for(;;)
{
//
if ( (buffer[0x04 + v.entry_length * index] >> 4) != DELETED_FILE)
{
unsigned offset = v.entry_length * index + 0x4;
FileEntry f;
f.Load(buffer + offset);
f.address = (block << 9) + offset;
files->push_back(f);
//if (++count == v.file_count) break;
}
index++;
if (index >= v.entries_per_block)
{
if (!next) break; // all done!
if (blocks.insert(next).second == false)
{
return -P8_CYCLICAL_BLOCK;
}
ok = Read(next, buffer);
if (ok < 0) return ok;
block = next;
prev = Read16(&buffer[0x00]);
next = Read16(&buffer[0x02]);
index = 0;
}
}
}
return 1;
}

84
ProDOS/Disk.h Normal file
View File

@ -0,0 +1,84 @@
/*
* Disk.h
* ProFUSE
*
* Created by Kelvin Sherlock on 12/18/08.
*
*/
#ifndef __DISK_H__
#define __DISK_H__
#include <fcntl.h>
#include <stdint.h>
#include <vector>
#include <ProDOS/File.h>
#include <Device/BlockDevice.h>
#include <memory>
#include <Common/smart_pointers.h>
enum {
P8_OK = 0,
P8_INTERNAL_ERROR,
P8_INVALID_FORK,
P8_INVALID_BLOCK,
P8_INVALID_STORAGE_TYPE,
P8_CYCLICAL_BLOCK
};
enum {
P8_DATA_FORK = 0,
P8_RESOURCE_FORK = 1
};
/* flags */
enum {
P8_DOS_ORDER = 1,
P8_2MG = 2,
P8_DC42 = 4
};
class Disk;
typedef SHARED_PTR(Disk) DiskPointer;
class Disk {
public:
~Disk();
//static Disk *Open2MG(const char *file);
static DiskPointer OpenFile(Device::BlockDevicePointer device);
int Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee = NULL);
int Read(unsigned block, void *buffer);
int ReadIndex(unsigned block, void *buffer, unsigned level, off_t offset, unsigned blocks);
int ReadFile(const FileEntry &f, void *buffer);
void *ReadFile(const FileEntry &f, unsigned fork, uint32_t *size, int * error);
int ReadVolume(VolumeEntry *volume, std::vector<FileEntry> *files);
int ReadDirectory(unsigned block, SubdirEntry *dir, std::vector<FileEntry> *files);
private:
Disk();
Disk(Device::BlockDevicePointer device);
unsigned _blocks;
Device::BlockDevicePointer _device;
};
#endif

132
ProDOS/Exception.cpp Normal file
View File

@ -0,0 +1,132 @@
#include "Exception.h"
#include <cstdio>
#include <cstring>
namespace ProDOS {
const char *Exception::errorString()
{
switch (error())
{
case badSystemCall:
return "Bad System Call";
case invalidPcount:
return "Invalid Parameter Count";
case gsosActive:
return "GS/OS Active";
case devNotFound:
return "Device Not Found";
case invalidDevNum:
return "Invalid Device Number";
case drvrBadReq:
return "Driver Bad Request";
case drvrBadCode:
return "Driver Bad Code";
case drvrBadParm:
return "Driver Bad Parameter";
case drvrNotOpen:
return "Driver Not Open";
case drvrPriorOpen:
return "Driver Prior Open";
case irqTableFull:
return "IRQ Table Full";
case drvrNoResrc:
return "Driver No Resource";
case drvrIOError:
return "Driver IO Error";
case drvrNoDevice:
return "Driver No Device";
case drvrBusy:
return "Driver Busy";
case drvrWrtProt:
return "Driver Write Protected";
case drvrBadCount:
return "Driver Bad Count";
case drvrBadBlock:
return "Driver Bad Block";
case drvrDiskSwitch:
return "Driver Disk Switch";
case drvrOffLine:
return "Driver Off Line";
case badPathSyntax:
return "Bad Path Syntax";
case invalidRefNum:
return "Invalid Ref Num";
case pathNotFound:
return "Path Not Found";
case volNotFound:
return "Volume Not Found";
case fileNotFound:
return "File Not Found";
case dupPathName:
return "Duplicate Path Name";
case volumeFull:
return "Volume Full";
case volDirFull:
return "Volume Directory Full";
case badFileFormat:
return "Bad File Format";
case badStoreType:
return "Bad Storage Type";
case eofEncountered:
return "End of File";
case outOfRange:
return "Out of Range";
case invalidAccess:
return "Invalid Access";
case buffTooSmall:
return "Buffer Too Small";
case fileBusy:
return "File Busy";
case dirError:
return "Directory Error";
case unknownVol:
return "Unknown Volume";
case paramRangeError:
return "Parameter Range Error";
case outOfMem:
return "Out of Memory";
case dupVolume:
return "Duplicate Volume";
case notBlockDev:
return "Not a Block Device";
case invalidLevel:
return "Invalid Level";
case damagedBitMap:
return "Damaged Bit Map";
case badPathNames:
return "Bad Path Names";
case notSystemFile:
return "Not a System File";
case osUnsupported:
return "OS Unsupported";
case stackOverflow:
return "Stack Overflow";
case dataUnavail:
return "Data Unavailable";
case endOfDir:
return "End Of Directory";
case invalidClass:
return "Invalid Class";
case resForkNotFound:
return "Resource Fork Not Found";
case invalidFSTID:
return "Invalid FST ID";
case devNameErr:
return "Device Name Error";
case resExistsErr:
return "Resource Exists Error";
case resAddErr:
return "Resource Add Error";
default:
return "";
}
return "";
}
}

97
ProDOS/Exception.h Normal file
View File

@ -0,0 +1,97 @@
#ifndef __PRODOS_EXCEPTION_H__
#define __PRODOS_EXCEPTION_H__
#include <Common/Exception.h>
namespace ProDOS {
// ProDOS Errors
enum
{
badSystemCall = 0x01,
invalidPcount = 0x04,
gsosActive = 0x07,
devNotFound = 0x10,
invalidDevNum = 0x11,
drvrBadReq = 0x20,
drvrBadCode = 0x21,
drvrBadParm = 0x22,
drvrNotOpen = 0x23,
drvrPriorOpen = 0x24,
irqTableFull = 0x25,
drvrNoResrc = 0x26,
drvrIOError = 0x27,
drvrNoDevice = 0x28,
drvrBusy = 0x29,
drvrWrtProt = 0x2b,
drvrBadCount = 0x2c,
drvrBadBlock = 0x2d,
drvrDiskSwitch = 0x2e,
drvrOffLine = 0x2f,
badPathSyntax = 0x40,
invalidRefNum = 0x43,
pathNotFound = 0x44,
volNotFound = 0x45,
fileNotFound = 0x46,
dupPathName = 0x47,
volumeFull = 0x48,
volDirFull = 0x49,
badFileFormat = 0x4a,
badStoreType = 0x4b,
eofEncountered = 0x4c,
outOfRange = 0x4d,
invalidAccess = 0x4e,
buffTooSmall = 0x4f,
fileBusy = 0x50,
dirError = 0x51,
unknownVol = 0x52,
paramRangeError = 0x53,
outOfMem = 0x54,
dupVolume = 0x57,
notBlockDev = 0x58,
invalidLevel = 0x59,
damagedBitMap = 0x5a,
badPathNames = 0x5b,
notSystemFile = 0x5c,
osUnsupported = 0x5d,
stackOverflow = 0x5f,
dataUnavail = 0x60,
endOfDir = 0x61,
invalidClass = 0x62,
resForkNotFound = 0x63,
invalidFSTID = 0x64,
devNameErr = 0x67,
resExistsErr = 0x70,
resAddErr = 0x71
};
class Exception : public ::Exception {
public:
Exception(const char *cp, int error);
Exception(const std::string& string, int error);
virtual const char *errorString();
private:
typedef ::Exception super;
};
inline Exception::Exception(const char *cp, int error) :
super(cp, error)
{
}
inline Exception::Exception(const std::string& string, int error) :
super(string, error)
{
}
}
#endif

231
ProDOS/File.cpp Normal file
View File

@ -0,0 +1,231 @@
/*
* File.cpp
* ProFUSE
*
* Created by Kelvin Sherlock on 12/18/08.
*
*/
#include <ProDOS/File.h>
#include <ProDOS/DateTime.h>
#include <Endian/Endian.h>
#include "common.h"
#include <cstring>
#include <cctype>
#include <cstdio>
#include <stdint.h>
using namespace LittleEndian;
bool FileEntry::Load(const void *data)
{
const uint8_t *cp = (const uint8_t *)data;
address = 0;
storage_type = cp[0x00] >> 4;
name_length = cp[0x00] & 0x0f;
memcpy(file_name, &cp[0x01], name_length);
file_name[name_length] = 0;
file_type = cp[0x10];
key_pointer = Read16(&cp[0x11]);
blocks_used = Read16(&cp[0x13]);
eof = Read24(&cp[0x15]);
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
//version = cp[0x1c];
//min_version = cp[0x1d];
unsigned xcase = Read16(&cp[0x1c]);
if (xcase & 0x8000)
{
// gsos technote #8
unsigned mask = 0x4000;
for (unsigned i = 0; i < name_length; i++)
{
if (xcase & mask) file_name[i] = tolower(file_name[i]);
mask = mask >> 1;
}
}
access = cp[0x1e];
aux_type = Read16(&cp[0x1f]);
last_mod = ProDOS::DateTime(Read16(&cp[0x21]), Read16(&cp[0x23]));
header_pointer = Read16(&cp[0x25]);
return true;
}
bool ExtendedEntry::Load(const void *data)
{
const uint8_t *cp = (const uint8_t *)data;
//prodos technote #25.
// offset 0 - mini entry for data fork
dataFork.storage_type = cp[0x00] & 0x0f;
dataFork.key_block = Read16(&cp[0x01]);
dataFork.blocks_used = Read16(&cp[0x03]);
dataFork.eof = Read24(&cp[0x05]);
// offset 256 - mini entry for resource fork.
resourceFork.storage_type = cp[256 + 0x00] & 0x0f;
resourceFork.key_block = Read16(&cp[256 + 0x01]);
resourceFork.blocks_used = Read16(&cp[256 + 0x03]);
resourceFork.eof = Read24(&cp[256 + 0x05]);
// xFInfo may be missing.
bzero(FInfo, sizeof(FInfo));
bzero(xFInfo, sizeof(xFInfo));
// size must be 18.
unsigned size;
unsigned entry;
for (unsigned i = 0; i < 2; i++)
{
unsigned ptr = i == 0 ? 8 : 26;
size = cp[ptr];
if (size != 18) continue;
entry = cp[ptr + 1];
switch(entry)
{
case 1:
memcpy(FInfo, &cp[ptr + 2], 16);
break;
case 2:
memcpy(xFInfo, &cp[ptr + 2], 16);
break;
}
}
//
return true;
}
bool VolumeEntry::Load(const void *data)
{
const uint8_t *cp = (const uint8_t *)data;
//prev_block = load16(&cp[0x00]);
//next_block = load16(&cp[0x02]);
storage_type = cp[0x00] >> 4;
name_length = cp[0x00] & 0x0f;
memcpy(volume_name, &cp[0x01], name_length);
volume_name[name_length] = 0;
// 0x14--0x1b reserved
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
last_mod = ProDOS::DateTime(Read16(&cp[0x12]), Read16(&cp[0x14]));
if (last_mod == 0) last_mod = creation;
//version = cp[0x1c];
//min_version = cp[0x1d];
unsigned xcase = Read16(&cp[0x16]);
if (xcase & 0x8000)
{
// gsos technote #8
unsigned mask = 0x4000;
for (unsigned i = 0; i < name_length; i++)
{
if (xcase & mask) volume_name[i] = tolower(volume_name[i]);
mask = mask >> 1;
}
}
access = cp[0x1e];
entry_length = cp[0x1f];
entries_per_block = cp[0x20];
file_count = Read16(&cp[0x21]);
bit_map_pointer = Read16(&cp[0x23]);
total_blocks = Read16(&cp[0x25]);
return true;
}
bool SubdirEntry::Load(const void *data)
{
const uint8_t *cp = (const uint8_t *)data;
//prev_block = load16(&cp[0x00]);
//next_block = load16(&cp[0x02]);
storage_type = cp[0x00] >> 4;
name_length = cp[0x00] & 0x0f;
memcpy(subdir_name, &cp[0x01], name_length);
subdir_name[name_length] = 0;
// 0x14 should be $14.
// 0x145-0x1b reserved
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
//version = cp[0x1c];
//min_version = cp[0x1d];
/*
unsigned xcase = load16(&cp[0x1c]);
if (xcase & 0x8000)
{
// gsos technote #8
unsigned mask = 0x4000;
for (unsigned i = 0; i < name_length; i++)
{
if (xcase & mask) subdir_name[i] = tolower(subdir_name[i]);
mask = mask >> 1;
}
}
*/
access = cp[0x1e];
entry_length = cp[0x1f];
entries_per_block = cp[0x20];
file_count = Read16(&cp[0x21]);
parent_pointer = Read16(&cp[0x23]);
parent_entry = cp[0x25];
parent_entry_length = cp[0x26];
return true;
}

133
ProDOS/File.h Normal file
View File

@ -0,0 +1,133 @@
/*
* File.h
* ProFUSE
*
* Created by Kelvin Sherlock on 12/18/08.
*
*/
#ifndef __PRODOS_FILE_H__
#define __PRODOS_FILE_H__
#include <time.h>
#include <stdint.h>
enum {
DELETED_FILE = 0,
SEEDLING_FILE = 1,
SAPLING_FILE = 2,
TREE_FILE = 3,
PASCAL_FILE = 4,
EXTENDED_FILE = 5,
DIRECTORY_FILE = 0x0d,
SUBDIR_HEADER = 0x0e,
VOLUME_HEADER = 0x0f
};
enum {
FILE_ENTRY_SIZE = 0x27,
};
enum {
ACCESS_DESTROY = 0x80,
ACCESS_RENAME = 0x40,
ACCESS_MODIFIED = 0x20,
ACCESS_WRITE = 0x02,
ACCRESS_READ = 0x01
};
class FileEntry {
public:
bool Load(const void *data);
unsigned storage_type;
unsigned name_length;
char file_name[15 + 1];
unsigned file_type;
unsigned key_pointer;
unsigned blocks_used;
uint32_t eof;
time_t creation;
//unsigned version;
//unsigned min_version;
unsigned access;
unsigned aux_type;
time_t last_mod;
unsigned header_pointer;
uint32_t address;
};
struct MiniEntry {
unsigned storage_type;
unsigned key_block;
unsigned blocks_used;
uint32_t eof;
};
class ExtendedEntry {
public:
bool Load(const void *data);
MiniEntry dataFork;
MiniEntry resourceFork;
uint8_t FInfo[16];
uint8_t xFInfo[16];
};
class VolumeEntry {
public:
bool Load(const void *data);
unsigned storage_type;
unsigned name_length;
char volume_name[15+1];
time_t creation;
time_t last_mod;
//unsigned version;
//unsigned min_version;
unsigned access;
unsigned entry_length;
unsigned entries_per_block;
unsigned file_count;
unsigned bit_map_pointer;
unsigned total_blocks;
friend class DirIter;
};
class SubdirEntry {
public:
bool Load(const void *data);
unsigned storage_type;
unsigned name_length;
char subdir_name[15+1];
time_t creation;
//unsigned version;
//unsigned min_version;
unsigned access;
unsigned entry_length;
unsigned entries_per_block;
unsigned file_count;
unsigned parent_pointer;
unsigned parent_entry;
unsigned parent_entry_length;
};
#endif

View File

@ -1,7 +0,0 @@
CC = g++
CPPFLAGS += -g -Wall -I../
all : DateTime.o
DateTime.o : DateTime.cpp DateTime.h

18
ProDOS/common.h Normal file
View File

@ -0,0 +1,18 @@
/*
* common.h
* ProFUSE
*
* Created by Kelvin Sherlock on 12/20/08.
*
*/
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdint.h>
#define BLOCK_SIZE 512
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
#include <ProFUSE/Exception.h>
using namespace ProFUSE;
Exception::~Exception() throw()
{
}
const char *Exception::what()
{
return _string.c_str();
}

View File

@ -1,92 +0,0 @@
#ifndef __EXCEPTION_H__
#define __EXCEPTION_H__
#include <string>
#include <exception>
namespace ProFUSE {
class Exception : public std::exception
{
public:
Exception(const char *cp);
Exception(const std::string &str);
virtual ~Exception() throw ();
virtual const char *what();
int error() const { return _error; }
protected:
Exception(const char *cp, int error);
Exception(const std::string& string, int error);
private:
int _error;
std::string _string;
};
class POSIXException : public Exception {
public:
POSIXException(const char *cp, int error);
POSIXException(const std::string& string, int error);
};
class ProDOSException : public Exception {
public:
ProDOSException(const char *cp, int error);
ProDOSException(const std::string& string, int error);
};
inline Exception::Exception(const char *cp):
_error(0),
_string(cp)
{
}
inline Exception::Exception(const std::string& string):
_error(0),
_string(string)
{
}
inline Exception::Exception(const char *cp, int error):
_error(error),
_string(cp)
{
}
inline Exception::Exception(const std::string& string, int error):
_error(error),
_string(string)
{
}
inline POSIXException::POSIXException(const char *cp, int error) :
Exception(cp, error)
{
}
inline POSIXException::POSIXException(const std::string& string, int error) :
Exception(string, error)
{
}
inline ProDOSException::ProDOSException(const char *cp, int error) :
Exception(cp, error)
{
}
inline ProDOSException::ProDOSException(const std::string& string, int error) :
Exception(string, error)
{
}
}
#endif

View File

@ -1,9 +0,0 @@
CC = g++
CPPFLAGS += -g -Wall -I../
all : Exception.o Lock.o
Exception.o : Exception.cpp Exception.h
Lock.o : Lock.cpp Lock.h

11
README.md Normal file
View File

@ -0,0 +1,11 @@
# profuse
ProDOS file system for FUSE
## End of Life Notice
This software, while usable, is EOL. No further updates are expected.
ProFUSE 2.0, featuring read/write support for ProDOS and HFS, is available as part of
[Golden Gate](http://golden-gate.ksherlock.com)
See the Wiki for details about the ProDOS file system.

366
apfm.cpp
View File

@ -1,366 +0,0 @@
/*
* FileMan utilities.
*
* L - list dir
* E -
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <unistd.h>
#include <strings.h>
#include <Pascal/File.h>
#include <Pascal/Date.h>
#include <Device/BlockDevice.h>
#include <Device/DiskCopy42Image.h>
const char *MonthName(unsigned m)
{
static const char *months[] = {
"",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
if (m > 12) return "";
return months[m];
}
const char *FileType(unsigned ft)
{
static const char *types[] = {
"Unknown",
"Badblocks",
"Codefile",
"Textfile",
"Infofile",
"Datafile",
"Graffile",
"Fotofile",
"SecureDir"
};
if (ft < 8) return types[ft];
return "";
}
void printUnusedEntry(unsigned block, unsigned size)
{
std::printf("< UNUSED > %4u %4u\n", size, block);
}
void printFileEntry(Pascal::FileEntry *e, bool extended)
{
Pascal::Date dt = e->modification();
if (extended)
{
std::printf("%-15s %4u %2u-%s-%2u %5u %5u %s\n",
e->name(),
e->blocks(),
dt.day(),
MonthName(dt.month()),
dt.year() % 100,
e->firstBlock(),
e->lastByte(),
FileType(e->fileKind())
);
}
else
{
std::printf("%-15s %4u %2u-%s-%2u\n",
e->name(),
e->blocks(),
dt.day(),
MonthName(dt.month()),
dt.year() % 100
);
}
}
int action_ls(int argc, char **argv, Pascal::VolumeEntry *volume)
{
//TODO -- check for -l flag.
bool extended = false;
unsigned fileCount = volume->fileCount();
unsigned used = volume->blocks();
unsigned max = 0;
unsigned volumeSize = volume->volumeBlocks();
unsigned lastBlock = volume->lastBlock();
int ch;
std::fprintf(stdout, "%s:\n", volume->name());
argv[0] = "afpm ls";
while ((ch = ::getopt(argc, argv, "l")) != -1)
{
switch(ch)
{
case 'l':
extended = true;
break;
}
}
argc -= optind;
argv += optind;
for (unsigned i = 0; i < fileCount; ++i)
{
Pascal::FileEntry *e = volume->fileAtIndex(i);
if (!e) continue;
if (lastBlock != e->firstBlock())
{
unsigned size = e->firstBlock() - lastBlock;
max = std::max(max, size);
if (extended)
{
printUnusedEntry(lastBlock, size);
}
}
printFileEntry(e, extended);
lastBlock = e->lastBlock();
used += e->blocks();
}
if (extended && (lastBlock != volumeSize))
{
unsigned size = volumeSize - lastBlock;
max = std::max(max, size);
printUnusedEntry(lastBlock, size);
}
std::fprintf(stdout,
"%u/%u files<listed/in-dir>, "
"%u blocks used, "
"%u unused, "
"%u in largest\n",
fileCount, fileCount,
used,
volumeSize - used,
max
);
return 0;
}
int action_cat(unsigned argc, char **argv, Pascal::VolumeEntry *volume)
{
// cat file1, file2...
argv[0] = "afpm cat";
if (argc < 2)
{
std::fprintf(stderr, "apfm cat: Please specify one or more files.");
return 1;
}
for (unsigned i = 0; i < argc; ++i)
{
const char *fname = argv[i];
unsigned fileSize;
unsigned offset;
uint8_t buffer[512];
Pascal::FileEntry *e = NULL;
// find it...
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
e = volume->fileAtIndex(i);
if (::strcasecmp(e->name(), fname) == 0) break;
e = NULL;
}
if (!e)
{
std::fprintf(stderr, "apfm cat: %s: no such file.\n", fname);
continue;
}
fileSize = e->fileSize();
offset = 0;
while (offset < fileSize)
{
unsigned count = std::min(512u, fileSize - offset);
e->read(buffer, count, offset);
std::fwrite(buffer, count, 1, stdout);
offset += count;
}
}
return 0;
}
int action_cp(int argc, char **argv, Pascal::VolumeEntry *volume)
{
return 0;
}
int action_mv(int argc, char **argv, Pascal::VolumeEntry *volume)
{
return 0;
}
int action_rm(int argc, char **argv, Pascal::VolumeEntry *volume)
{
return 0;
}
int action_krunch(int argc, char **argv, Pascal::VolumeEntry *volume)
{
return 0;
}
void usage()
{
std::printf(
"Pascal File Manager v 0.0\n\n"
"Usage: fileman [-h] [-f format] diskimage action ...\n"
"Options:\n"
" -h Show usage information.\n"
" -f format Specify disk format. Valid values are:\n"
" po: ProDOS order disk image\n"
" do: DOS Order disk image\n"
"\n"
"Actions:\n"
" cat\n"
" cp\n"
" krunch\n"
" ls\n"
" mv\n"
" rm\n"
);
}
int main(int argc, char **argv)
{
std::auto_ptr<Pascal::VolumeEntry> volume;
std::auto_ptr<Device::BlockDevice> device;
unsigned fmt = 0;
int c;
// getop stops at first non '-' arg so it will not affect action flags.
while ((c = ::getopt(argc, argv, "f:h")) != -1)
{
std::printf("%c\n", c);
switch(c)
{
case 'f':
fmt = Device::DiskImage::ImageType(optarg);
if (!fmt)
{
std::fprintf(stderr, "Error: Invalid file format: ``%s''.\n",
optarg);
}
break;
case 'h':
case '?':
case ':':
usage();
return c == 'h' ? 0 : 1;
}
}
argc -= optind;
argv += optind;
optreset = 1;
optind = 1;
if (argc != 2)
{
usage();
return 0;
}
const char *file = argv[0];
const char *action = argv[1];
if (!fmt) fmt = Device::DiskImage::ImageType(optarg, 'PO__');
try {
switch(fmt)
{
case 'DO__':
device.reset( new Device::DOSOrderDiskImage(file, true) );
break;
case 'PO__':
device.reset( new Device::ProDOSOrderDiskImage(file, true) );
break;
case 'DC42':
device.reset( new Device::DiskCopy42Image(file, true) );
break;
default:
std::fprintf(stderr, "Unable to determine format. Please use -f flag.\n");
return 2;
}
volume.reset( new Pascal::VolumeEntry(device.get()));
device.release();
if (!::strcasecmp("cat", action)) return action_cat(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("cp", action)) return action_cp(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("krunch", action)) return action_krunch(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("ls", action)) return action_ls(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("mv", action)) return action_mv(argc - 1, argv + 1, volume.get());
if (!::strcasecmp("rm", action)) return action_rm(argc - 1, argv + 1, volume.get());
usage();
return 3;
}
catch (ProFUSE::Exception& e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", strerror(e.error()));
}
return 0;
}

1077
bin/apfm.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstddef>
#include <vector>
#include <string>
@ -9,26 +10,24 @@
#include <unistd.h>
#define __FreeBSD__ 10
#define __DARWIN_64_BIT_INO_T 1
#define _FILE_OFFSET_BITS 64
#define FUSE_USE_VERSION 27
#include <fuse/fuse_opt.h>
#include <fuse/fuse_lowlevel.h>
#include <fuse_opt.h>
#include <fuse_lowlevel.h>
#include <Pascal/Pascal.h>
#include <Common/Exception.h>
#include <Pascal/File.h>
#include <ProFUSE/Exception.h>
#include <File/File.h>
#include <Device/Device.h>
#include <Device/BlockDevice.h>
#include <Device/MappedFile.h>
#include <Device/DiskCopy42Image.h>
Pascal::VolumeEntry *fVolume = NULL;
std::string fDiskImage;
void usage()
{
std::printf("profuse_pascal 0.1\n\n");
@ -42,6 +41,11 @@ void usage()
" -v verbose\n"
" --format=format specify the disk image format. Valid values are:\n"
" dc42 DiskCopy 4.2 Image\n"
" davex Davex Disk Image\n"
" 2img Universal Disk Image\n"
#ifdef HAVE_NUFX
" sdk ShrinkIt Disk Image\n"
#endif
" do DOS Order Disk Image\n"
" po ProDOS Order Disk Image (default)\n"
" -o opt1,opt2... other mount parameters.\n"
@ -152,17 +156,23 @@ bool make_mount_dir(std::string name, std::string &path)
int main(int argc, char **argv)
{
extern void init_ops(fuse_lowlevel_ops *ops);
struct options options = { 0 };
struct options options;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
char *mountpoint = NULL;
int err = -1;
std::string mountPath;
unsigned format;
std::memset(&options, 0, sizeof(options));
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
char *mountpoint = NULL;
int err = -1;
std::string mountPath;
unsigned format = 0;
int foreground = false;
int multithread = false;
Pascal::VolumeEntryPointer volume;
init_ops(&pascal_ops);
@ -180,56 +190,34 @@ int main(int argc, char **argv)
// default prodos-order disk image.
if (options.format)
{
format = Device::DiskImage::ImageType(options.format);
format = Device::BlockDevice::ImageType(options.format);
if (!format)
std::fprintf(stderr, "Warning: Unknown image type ``%s''\n", options.format);
}
if (!format)
format = Device::DiskImage::ImageType(fDiskImage.c_str(), 'PO__');
bool readOnly = true;
try {
std::auto_ptr<Device::BlockDevice> device;
try
{
Device::BlockDevicePointer device;
device = Device::BlockDevice::Open(fDiskImage.c_str(), File::ReadOnly, format);
switch(format)
if (!device.get())
{
case 'DC42':
device.reset(new Device::DiskCopy42Image(fDiskImage.c_str(), readOnly));
break;
case 'PO__':
device.reset(new Device::ProDOSOrderDiskImage(fDiskImage.c_str(), readOnly));
break;
case 'DO__':
device.reset(new Device::DOSOrderDiskImage(fDiskImage.c_str(), readOnly));
break;
default:
std::fprintf(stderr, "Error: Unknown or unsupported device type.\n");
exit(1);
}
fVolume = new Pascal::VolumeEntry(device.get());
device.release();
volume = Pascal::VolumeEntry::Open(device);
}
catch (ProFUSE::POSIXException &e)
catch (::Exception &e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", std::strerror(e.error()));
return -1;
}
catch (ProFUSE::Exception &e)
{
std::fprintf(stderr, "%s\n", e.what());
return -1;
}
@ -237,7 +225,7 @@ int main(int argc, char **argv)
{
// Macfuse supports custom volume names (displayed in Finder)
std::string str("-ovolname=");
str += fVolume->name();
str += volume->name();
fuse_opt_add_arg(&args, str.c_str());
// 512 byte blocksize.
@ -266,7 +254,7 @@ int main(int argc, char **argv)
if (mountpoint == NULL || *mountpoint == 0)
{
if (make_mount_dir(fVolume->name(), mountPath))
if (make_mount_dir(volume->name(), mountPath))
mountpoint = (char *)mountPath.c_str();
}
@ -277,15 +265,14 @@ int main(int argc, char **argv)
{
struct fuse_session* se;
std::printf("Mounting ``%s'' on ``%s''\n", fVolume->name(), mountpoint);
std::printf("Mounting ``%s'' on ``%s''\n", volume->name(), mountpoint);
se = fuse_lowlevel_new(&args, &pascal_ops, sizeof(pascal_ops), fVolume);
se = fuse_lowlevel_new(&args, &pascal_ops, sizeof(pascal_ops), volume.get());
if (se) do {
//foreground = 1;
//multithread = 0;
err = fuse_daemonize(foreground); // todo
err = fuse_daemonize(foreground);
if (err < 0 ) break;
err = fuse_set_signal_handlers(se);
@ -307,7 +294,6 @@ int main(int argc, char **argv)
fuse_opt_free_args(&args);
delete fVolume;
#ifdef __APPLE__
@ -316,4 +302,4 @@ int main(int argc, char **argv)
return err ? 1 : 0;
}
}

View File

@ -1,27 +1,28 @@
#define __FreeBSD__ 10
#define __DARWIN_64_BIT_INO_T 1
#define _FILE_OFFSET_BITS 64
#define FUSE_USE_VERSION 27
#include <string>
#include <vector>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/stat.h>
#include <unistd.h>
#include <fuse/fuse_opt.h>
#include <fuse/fuse_lowlevel.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <Pascal/File.h>
#include <ProFUSE/auto.h>
#include <ProFUSE/Exception.h>
#include <fuse_opt.h>
#include <fuse_lowlevel.h>
#include <Pascal/Pascal.h>
#include <Common/auto.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
#define NO_ATTR() \
{ \
@ -42,55 +43,105 @@
}
#define DEBUGNAME() \
if (0) { std::fprintf(stderr, "%s\n", __func__); }
// linux doesn't have ENOATTR.
#ifndef ENOATTR
#define ENOATTR ENOENT
#endif
using namespace Pascal;
static FileEntry *findChild(VolumeEntry *volume, unsigned inode)
// fd_table is files which have been open.
// fd_table_available is a list of indexes in fd_table which are not currently used.
static std::vector<FileEntryPointer> fd_table;
static std::vector<unsigned> fd_table_available;
static FileEntryPointer findChild(VolumeEntry *volume, unsigned inode)
{
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
FileEntry *child = volume->fileAtIndex(i);
FileEntryPointer child = volume->fileAtIndex(i);
if (!child) continue;
if (inode == child->inode()) return child;
}
return NULL;
return FileEntryPointer();
}
#pragma mark -
#pragma mark fs
static void pascal_init(void *userdata, struct fuse_conn_info *conn)
{
//std::printf("pascal_init\n");
DEBUGNAME()
// nop
// text files have a non-thread safe index.
// which is initialized via read() or fileSize()
VolumeEntry *volume = (VolumeEntry *)userdata;
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
volume->fileAtIndex(i)->fileSize();
FileEntryPointer child = volume->fileAtIndex(i);
child->fileSize();
}
}
static void pascal_destroy(void *userdata)
{
//std::printf("pascal_destroy\n");
DEBUGNAME()
// nop
}
static void pascal_statfs(fuse_req_t req, fuse_ino_t ino)
{
DEBUGNAME()
struct statvfs vst;
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
ERROR(!volume, EIO)
// returns statvfs for the mount path or any file in the fs
// therefore, ignore ino.
std::memset(&vst, 0, sizeof(vst));
vst.f_bsize = 512; // fs block size
vst.f_frsize = 512; // fundamental fs block size
vst.f_blocks = volume->volumeBlocks();
vst.f_bfree = volume->freeBlocks(true); // free blocks
vst.f_bavail = volume->freeBlocks(false); // free blocks (non-root)
vst.f_files = volume->fileCount();
vst.f_ffree = -1; // free inodes.
vst.f_favail = -1; // free inodes (non-root)
vst.f_fsid = 0; // file system id?
vst.f_flag = volume->readOnly() ? ST_RDONLY | ST_NOSUID : ST_NOSUID;
vst.f_namemax = 15;
fuse_reply_statfs(req, &vst);
}
#pragma mark -
#pragma mark xattr
static void pascal_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
//std::printf("pascal_listxattr\n");
DEBUGNAME()
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file;
FileEntryPointer file;
std::string attr;
unsigned attrSize;
@ -130,11 +181,11 @@ static void pascal_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
{
//std::printf("pascal_getxattr\n");
DEBUGNAME()
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file;
FileEntryPointer file;
std::string attr(name);
ERROR(ino == 1, ENOATTR)
@ -177,6 +228,8 @@ static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, si
// OS X version.
static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t off)
{
DEBUGNAME()
pascal_getxattr(req, ino, name, size);
}
@ -185,8 +238,7 @@ static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, si
static void pascal_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
//std::printf("pascal_opendir\n");
DEBUGNAME()
ERROR(ino != 1, ENOTDIR)
@ -197,17 +249,17 @@ static void pascal_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
static void pascal_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
//std::printf("pascal_releasedir\n");
DEBUGNAME()
fuse_reply_err(req, 0);
}
static void pascal_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
//std::printf("pascal_readdir %u, %u, %u\n", (unsigned)ino, (unsigned)size, (unsigned)off);
DEBUGNAME()
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
ProFUSE::auto_array<uint8_t> buffer(new uint8_t[size]);
::auto_array<uint8_t> buffer(new uint8_t[size]);
unsigned count = volume->fileCount();
@ -253,7 +305,7 @@ static void pascal_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t of
{
unsigned tmp;
FileEntry *file = volume->fileAtIndex(i);
FileEntryPointer file = volume->fileAtIndex(i);
if (file == NULL) break; //?
// only these fields are used.
@ -283,6 +335,8 @@ static void pascal_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t of
static void stat(FileEntry *file, struct stat *st)
{
DEBUGNAME()
std::memset(st, 0, sizeof(struct stat));
time_t t = file->modification();
@ -302,6 +356,8 @@ static void stat(FileEntry *file, struct stat *st)
static void stat(VolumeEntry *volume, struct stat *st)
{
DEBUGNAME()
std::memset(st, 0, sizeof(struct stat));
time_t t = volume->lastBoot();
@ -321,7 +377,7 @@ static void stat(VolumeEntry *volume, struct stat *st)
static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
//std::printf("pascal_lookup %u %s\n", (unsigned)parent, name);
DEBUGNAME()
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
struct fuse_entry_param entry;
@ -332,7 +388,7 @@ static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
FileEntry *file = volume->fileAtIndex(i);
FileEntryPointer file = volume->fileAtIndex(i);
if (file == NULL) break;
if (::strcasecmp(file->name(), name)) continue;
@ -343,7 +399,7 @@ static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
entry.entry_timeout = 0.0;
entry.ino = file->inode();
stat(file, &entry.attr);
stat(file.get(), &entry.attr);
fuse_reply_entry(req, &entry);
return;
}
@ -354,11 +410,11 @@ static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
//std::printf("pascal_getattr %u\n", (unsigned)ino);
DEBUGNAME()
struct stat st;
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file;
FileEntryPointer file;
if (ino == 1)
{
@ -370,9 +426,9 @@ static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
file = findChild(volume, ino);
ERROR(file == NULL, ENOENT)
printf("\t%s\n", file->name());
//printf("\t%s\n", file->name());
stat(file, &st);
stat(file.get(), &st);
fuse_reply_attr(req, &st, 0.0);
}
@ -382,10 +438,11 @@ static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
static void pascal_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
//std::printf("pascal_open\n");
unsigned index;
DEBUGNAME()
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file;
FileEntryPointer file;
ERROR(ino == 1, EISDIR)
@ -395,7 +452,20 @@ static void pascal_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f
ERROR((fi->flags & O_ACCMODE) != O_RDONLY, EACCES)
fi->fh = (uint64_t)file;
// insert the FileEntryPointer into fd_table.
if (fd_table_available.size())
{
index = fd_table_available.back();
fd_table_available.pop_back();
fd_table[index] = file;
}
else
{
index = fd_table.size();
fd_table.push_back(file);
}
fi->fh = index;
fuse_reply_open(req, fi);
}
@ -403,7 +473,11 @@ static void pascal_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f
static void pascal_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
//std::printf("pascal_release\n");
unsigned index = fi->fh;
DEBUGNAME()
fd_table[index].reset();
fd_table_available.push_back(index);
fuse_reply_err(req, 0);
}
@ -411,22 +485,26 @@ static void pascal_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
static void pascal_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
//std::printf("pascal_read %u %u %u\n", (unsigned)ino, (unsigned)size, (unsigned)off);
unsigned index = fi->fh;
DEBUGNAME()
//VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file = (FileEntry *)fi->fh;
FileEntryPointer file = fd_table[index];
try
{
ProFUSE::auto_array<uint8_t> buffer(new uint8_t[size]);
::auto_array<uint8_t> buffer(new uint8_t[size]);
unsigned rsize = file->read(buffer.get(), size, off);
fuse_reply_buf(req, (char *)(buffer.get()), rsize);
return;
}
catch (ProFUSE::POSIXException &e)
catch (POSIX::Exception &e)
{
printf("posix error...\n");
ERROR(true, e.error());
@ -439,14 +517,15 @@ static void pascal_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
}
void init_ops(fuse_lowlevel_ops *ops)
{
DEBUGNAME()
std::memset(ops, 0, sizeof(fuse_lowlevel_ops));
ops->init = pascal_init;
ops->destroy = pascal_destroy;
ops->statfs = pascal_statfs;
// returns pascal.filekind, text encoding.
ops->listxattr = pascal_listxattr;

314
bin/newfs_pascal.cpp Normal file
View File

@ -0,0 +1,314 @@
#include <memory>
#include <new>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <unistd.h>
#include <sys/stat.h>
#include <Device/BlockDevice.h>
#include <Device/RawDevice.h>
#include <Common/Exception.h>
#include <Pascal/Pascal.h>
#include <File/File.h>
#include <File/MappedFile.h>
using namespace Pascal;
using namespace Device;
#define NEWFS_VERSION "0.1"
bool yes_or_no()
{
int ch, first;
(void)fflush(stderr);
first = ch = getchar();
while (ch != '\n' && ch != EOF)
ch = getchar();
return (first == 'y' || first == 'Y');
}
/*
* \d+ by block
* \d+[Kk] by kilobyte
* \d+[Mm] by megabyte
*/
unsigned parseBlocks(const char *cp)
{
unsigned long blocks = 0;
char *mod;
errno = 0;
blocks = std::strtoul(cp, &mod, 10);
if (errno) return -1;
if (mod == cp) return -1;
if (blocks > 0xffff) return -1;
if (mod)
{
switch(*mod)
{
case 0:
break;
case 'm': // 1m = 1024*1024b = 2048 blocks
case 'M':
blocks *= 2048;
break;
case 'k': // 1k = 1024b = 2 blocks
case 'K':
blocks *= 2;
break;
}
if (blocks > 0xffff) return -1;
}
return (unsigned)blocks;
}
// return the basename, without an extension.
std::string filename(const std::string& src)
{
unsigned start;
unsigned end;
if (src.empty()) return std::string("");
start = end = 0;
for(unsigned i = 0, l = src.length(); i < l; ++i)
{
char c = src[i];
if (c == '/') start = end = i + 1;
if (c == '.') end = i;
}
if (start == src.length()) return std::string("");
if (start == end) return src.substr(start);
return src.substr(start, end - start);
}
void usage()
{
std::printf("newfs_pascal %s\n", NEWFS_VERSION);
std::printf("\n");
std::printf("newfs_pascal [-v volume_name] [-s size] [-f format] file\n");
std::printf("\n");
std::printf(" -v volume_name Specify the volume name.\n"
" Default is Untitled.\n"
" -s size Specify size in blocks.\n"
" Default is 1600 blocks (800K)\n"
" -b bootfile Specify a file that contains the boot block\n"
" -f format Specify the disk image format. Valid values are:\n"
" 2img Universal Disk Image\n"
" dc42 DiskCopy 4.2 Image\n"
" davex Davex Disk Image\n"
" do DOS Order Disk Image\n"
" po ProDOS Order Disk Image (default)\n"
);
}
int main(int argc, char **argv)
{
unsigned blocks = 1600;
std::string volumeName;
std::string fileName;
std::string bootFile;
int format = 0;
const char *fname;
int c;
while ( (c = ::getopt(argc, argv, "hf:s:v:")) != -1)
{
switch(c)
{
case 'h':
default:
usage();
return c == 'h' ? 0 : 1;
break;
case 'v':
volumeName = optarg;
// make sure it's legal.
if (!VolumeEntry::ValidName(optarg))
{
std::fprintf(stderr, "Error: `%s' is not a valid Pascal volume name.\n", optarg);
return 0x40;
}
break;
case 's':
blocks = parseBlocks(optarg);
if (blocks > 0xffff)
{
std::fprintf(stderr, "Error: `%s' is not a valid disk size.\n", optarg);
return 0x5a;
}
break;
case 'f':
format = Device::BlockDevice::ImageType(optarg);
if (format == 0)
{
std::fprintf(stderr, "Error: `%s' is not a supported disk image format.\n", optarg);
return -1;
}
break;
case 'b':
bootFile = optarg;
break;
}
}
argc -= optind;
argv += optind;
if (argc != 1)
{
usage();
return -1;
}
fname = argv[0];
fileName = argv[0];
try
{
struct stat st;
bool rawDevice = false;
BlockDevicePointer device;
VolumeEntryPointer volume;
// Check for block device. if so, verify.
// if file exists, verify before overwrite.
std::memset(&st, 0, sizeof(st));
if (::stat(fname, &st) == 0)
{
if (S_ISBLK(st.st_mode))
{
fprintf(stderr, "`%s' is a raw device. Are you sure you want to initialize it? ", fname);
if (!yes_or_no()) return -1;
device = RawDevice::Open(fname, File::ReadWrite);
blocks = device->blocks();
rawDevice = true;
if (blocks > 0xffff)
{
std::fprintf(stderr, "Error: device is too large.\n");
return 0x5a;
}
}
else
{
// file exists, verify we want to destroy it.
fprintf(stderr, "`%s' already exists. Are you sure you want to overwrite it? ", fname);
if (!yes_or_no()) return -1;
}
}
// generate a filename.
if (volumeName.empty())
{
if (!rawDevice)
volumeName = filename(fileName);
if (volumeName.empty() || !VolumeEntry::ValidName(volumeName.c_str()))
volumeName = "PASCAL";
}
if (!rawDevice)
device = BlockDevice::Create(fname, volumeName.c_str(), blocks, format);
if (!device.get())
{
std::fprintf(stderr, "Error: Unsupported diskimage format.\n");
return -1;
}
if (!bootFile.empty())
{
MappedFile bf(bootFile.c_str(), File::ReadOnly, std::nothrow);
if (!bf.isValid())
{
std::fprintf(stderr, "Warning: unable to open boot file `%s'.\n", bootFile.c_str());
}
else
{
size_t length = bf.length();
// either 1 or 2 blocks.
if (length == 512)
{
device->write(0, bf.address());
}
else if (length == 1024)
{
device->write(0, bf.address());
device->write(1, (uint8_t*)bf.address() + 512);
}
else
{
std::fprintf(stderr, "Warning: boot file must be 512 or 1024 bytes.\n");
}
}
}
volume = VolumeEntry::Create(device, volumeName.c_str());
}
catch (::Exception& e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", ::strerror(e.error()));
return -2;
}
return 0;
}

View File

@ -7,18 +7,14 @@
#include <unistd.h>
#include "BlockDevice.h"
#include "DavexDiskImage.h"
#include "DiskCopy42Image.h"
#include "Entry.h"
#include "Exception.h"
#include "RawDevice.h"
#include "UniversalDiskImage.h"
#include <Device/BlockDevice.h>
#include <ProFUSE/Exception.h>
#define NEWFS_VERSION "0.1"
using namespace ProFUSE;
using namespace Device;
void usage()
{
@ -173,7 +169,7 @@ int main(int argc, char **argv)
case 'f':
{
format = DiskImage::ImageType(optarg);
format = BlockDevice::ImageType(optarg);
if (format == 0)
{
std::fprintf(stderr, "Error: `%s' is not a supported disk image format.\n", optarg);
@ -204,7 +200,7 @@ int main(int argc, char **argv)
volumeName = "Untitled";
}
if (format == 0) format = DiskImage::ImageType(fname, '2IMG');
if (format == 0) format = BlockDevice::ImageType(fname, '2IMG');
try

374
bin/profuse.cpp Normal file
View File

@ -0,0 +1,374 @@
/*
* main.cpp
* ProFUSE
*
* Created by Kelvin Sherlock on 12/24/08.
*
*/
/*
#define __FreeBSD__ 10
#define _FILE_OFFSET_BITS 64
#define __DARWIN_64_BIT_INO_T 1
#define _REENTRANT
#define _POSIX_C_SOURCE 200112L
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstddef>
#include <vector>
#include <string>
#include <Device/BlockDevice.h>
#include "profuse.h"
using std::vector;
using std::string;
/*
* globals variables.
*
*/
std::string fDiskImage;
DiskPointer disk;
VolumeEntry volume;
bool validProdosName(const char *name)
{
// OS X looks for hidden files that don't exist (and aren't legal prodos names)
// most are not legal prodos names, so this filters them out easily.
// [A-Za-z][0-9A-Za-z.]{0,14}
if (!isalpha(*name)) return false;
unsigned i;
for(i = 1; name[i]; i++)
{
char c = name[i];
if (c == '.' || isalnum(c)) continue;
return false;
}
return i < 16;
}
static struct fuse_lowlevel_ops prodos_oper;
enum {
PRODOS_OPT_HELP,
PRODOS_OPT_VERSION,
PRODOS_OPT_WRITE,
PRODOS_OPT_FORMAT,
PRODOS_OPT_VERBOSE
};
struct options {
char *format;
int readOnly;
int readWrite;
int verbose;
int debug;
} options;
#define PRODOS_OPT_KEY(T, P, V) {T, offsetof(struct options, P), V}
static struct fuse_opt prodos_opts[] = {
FUSE_OPT_KEY("-h", PRODOS_OPT_HELP),
FUSE_OPT_KEY("--help", PRODOS_OPT_HELP),
FUSE_OPT_KEY("-V", PRODOS_OPT_VERSION),
FUSE_OPT_KEY("--version", PRODOS_OPT_VERSION),
PRODOS_OPT_KEY("-v", verbose, 1),
PRODOS_OPT_KEY("-w", readWrite, 1),
PRODOS_OPT_KEY("rw", readWrite, 1),
PRODOS_OPT_KEY("-d", debug, 1),
PRODOS_OPT_KEY("--format=%s", format, 0),
PRODOS_OPT_KEY("format=%s", format, 0),
{0, 0, 0}
};
static void usage()
{
fprintf(stderr, "profuse [options] disk_image [mountpoint]\n"
"Options:\n"
" -d debug\n"
" -r readonly\n"
" -w mount writable [not yet]\n"
" -v verbose\n"
" --format=format specify the disk image format. Valid values are:\n"
" dc42 DiskCopy 4.2 Image\n"
" davex Davex Disk Image\n"
#ifdef HAVE_NUFX
" sdk ShrinkIt Disk Image\n"
#endif
" 2img Universal Disk Image\n"
" do DOS Order Disk Image\n"
" po ProDOS Order Disk Image (default)\n"
" -o opt1,opt2... other mount parameters.\n"
);
}
static int prodos_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
{
switch(key)
{
case PRODOS_OPT_HELP:
usage();
exit(0);
break;
case PRODOS_OPT_VERSION:
// TODO
exit(1);
break;
case FUSE_OPT_KEY_NONOPT:
// first arg is the disk image.
if (fDiskImage.empty())
{
fDiskImage = arg;
return 0;
}
return 1;
}
return 1;
}
#ifdef __APPLE__
// create a dir in /Volumes/diskname.
bool make_mount_dir(string name, string &path)
{
path = "";
if (name.find('/') != string::npos) return false;
if (name.find('\\') != string::npos) return false;
if (name.find(':') != string::npos) return false;
path = "";
path = "/Volumes/" + name;
rmdir(path.c_str());
if (mkdir(path.c_str(), 0777) == 0) return true;
for (unsigned i = 0; i < 26; i++)
{
path = "/Volumes/" + name + " " + (char)('a' + i);
rmdir(path.c_str());
if (mkdir(path.c_str(), 0777) == 0) return true;
}
path = "";
return false;
}
#endif
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
char *mountpoint = NULL;
int err = -1;
struct options options;
unsigned format = 0;
int foreground = false;
int multithread = false;
#if __APPLE__
string mountpath;
#endif
std::memset(&prodos_oper, 0, sizeof(prodos_oper));
std::memset(&options, 0, sizeof(options));
prodos_oper.listxattr = prodos_listxattr;
prodos_oper.getxattr = prodos_getxattr;
prodos_oper.opendir = prodos_opendir;
prodos_oper.releasedir = prodos_releasedir;
prodos_oper.readdir = prodos_readdir;
prodos_oper.lookup = prodos_lookup;
prodos_oper.getattr = prodos_getattr;
prodos_oper.open = prodos_open;
prodos_oper.release = prodos_release;
prodos_oper.read = prodos_read;
prodos_oper.statfs = prodos_statfs;
// scan the argument list, looking for the name of the disk image.
if (fuse_opt_parse(&args, &options , prodos_opts, prodos_opt_proc) == -1)
exit(1);
if (fDiskImage.empty())
{
usage();
exit(1);
}
// default prodos-order disk image.
if (options.format)
{
format = Device::BlockDevice::ImageType(options.format);
if (!format)
std::fprintf(stderr, "Warning: Unknown image type ``%s''\n", options.format);
}
try {
Device::BlockDevicePointer device;
device = Device::BlockDevice::Open(fDiskImage.c_str(), File::ReadOnly, format);
if (!device)
{
std::fprintf(stderr, "Error: Unknown or unsupported device type.\n");
exit(1);
}
disk = Disk::OpenFile(device);
if (!disk)
{
fprintf(stderr, "Unable to mount disk %s\n", fDiskImage.c_str());
exit(1);
}
}
catch (::Exception &e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", e.errorString());
return -1;
}
disk->ReadVolume(&volume, NULL);
#ifdef __APPLE__
{
// Macfuse supports custom volume names (displayed in Finder)
string str="-ovolname=";
str += volume.volume_name;
fuse_opt_add_arg(&args, str.c_str());
}
#endif
// use 512byte blocks.
#if __APPLE__
fuse_opt_add_arg(&args, "-oiosize=512");
#endif
do {
if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) break;
#ifdef __APPLE__
if (mountpoint == NULL || *mountpoint == 0)
{
if (make_mount_dir(volume.volume_name, mountpath))
mountpoint = (char *)mountpath.c_str();
}
#endif
foreground = options.debug;
if (mountpoint == NULL || *mountpoint == 0)
{
fprintf(stderr, "no mount point\n");
break;
}
if ( (ch = fuse_mount(mountpoint, &args)) != NULL)
{
struct fuse_session *se;
se = fuse_lowlevel_new(&args, &prodos_oper, sizeof(prodos_oper), NULL);
if (se != NULL) do {
err = fuse_daemonize(foreground);
if (err < 0 ) break;
err = fuse_set_signal_handlers(se);
if (err < 0) break;
fuse_session_add_chan(se, ch);
if (multithread) err = fuse_session_loop_mt(se);
else err = fuse_session_loop(se);
fuse_remove_signal_handlers(se);
fuse_session_remove_chan(ch);
} while (false);
if (se) fuse_session_destroy(se);
fuse_unmount(mountpoint, ch);
}
} while (false);
fuse_opt_free_args(&args);
disk.reset();
#ifdef __APPLE__
if (mountpath.size()) rmdir(mountpath.c_str());
#endif
return err ? 1 : 0;
}

56
bin/profuse.h Normal file
View File

@ -0,0 +1,56 @@
/*
* profuse.h
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#ifndef __PROFUSE_H__
#define __PROFUSE_H__
#include <ProDOS/File.h>
#include <ProDOS/Disk.h>
#include <ProDOS/common.h>
#define FUSE_USE_VERSION 27
#include <fuse_opt.h>
#include <fuse_lowlevel.h>
#undef ERROR
#define ERROR(cond,errno) if ( (cond) ){ fuse_reply_err(req, errno); return; }
extern DiskPointer disk;
bool validProdosName(const char *name);
// xattr
void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t off);
void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size);
void prodos_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size);
//dirent
void prodos_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi);
// stat
void prodos_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_lookup(fuse_req_t req, fuse_ino_t parent, const char *name);
void prodos_statfs(fuse_req_t req, fuse_ino_t ino);
// file io.
void prodos_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
void prodos_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi);
#endif

132
bin/profuse_dirent.cpp Normal file
View File

@ -0,0 +1,132 @@
/*
* profuse_dirent.cpp
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#include "profuse.h"
#include <strings.h>
#include <cstdio>
#include <cerrno>
#include <algorithm>
#include <string>
#include <vector>
using std::string;
using std::vector;
#pragma mark Directory Functions
/*
* when the directory is opened, we load the volume/directory and store the FileEntry vector into
* fi->fh.
*
*/
void prodos_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
fprintf(stderr, "opendir: %u\n", (unsigned)ino);
// verify it's a directory/volume here?
uint8_t buffer[BLOCK_SIZE];
vector<FileEntry> files;
bool ok;
ok = disk->Read(ino == 1 ? 2 : ino >> 9, buffer);
ERROR(ok < 0, EIO)
if (ino == 1)
{
VolumeEntry v;
v.Load(buffer + 0x04);
ok = disk->ReadVolume(&v, &files);
ERROR(ok < 0, EIO)
}
else
{
FileEntry e;
e.Load(buffer + (ino & 0x1ff));
ERROR(e.storage_type != DIRECTORY_FILE, ENOTDIR)
ok = disk->ReadDirectory(e.key_pointer, NULL, &files);
ERROR(ok < 0, EIO);
}
// copy the vector contents to a vector *.
vector<FileEntry> *fp = new vector<FileEntry>();
files.swap(*fp);
fi->fh = (uint64_t)fp;
fuse_reply_open(req, fi);
}
void prodos_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
fprintf(stderr,"releasedir: %u\n", (unsigned)ino);
vector<FileEntry> *files = (vector<FileEntry> *)fi->fh;
if (files) delete files;
fuse_reply_err(req, 0);
}
void prodos_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
vector<FileEntry> *files = (vector<FileEntry> *)fi->fh;
struct stat st;
fprintf(stderr, "readdir %u %u %u\n", (unsigned)ino, (unsigned)size, (unsigned)off);
// TODO -- add "." and ".." entries...
// if the offset >= number of entries, get out.
if (!files || files->size() <= off)
{
fprintf(stderr, "fuse_reply_buf(req, NULL, 0)\n");
fuse_reply_buf(req, NULL, 0);
return;
}
// now some dirent info...
bzero(&st, sizeof(st));
// only mode and ino are used.
char *buffer = new char[size];
unsigned count = files->size();
unsigned current_size = 0;
for (unsigned i = off; i < count; ++i)
{
FileEntry &f = (*files)[i];
st.st_mode = f.storage_type == DIRECTORY_FILE ? S_IFDIR | 0555 : S_IFREG | 0444;
st.st_ino = f.address;
unsigned entry_size = fuse_add_direntry(req, NULL, 0, f.file_name, NULL, 0);
if (entry_size + current_size >= size) break;
fuse_add_direntry(req, (char *)buffer + current_size, size, f.file_name, &st, i + 1);
current_size += entry_size;
}
fuse_reply_buf(req, buffer, current_size);
delete []buffer;
}

136
bin/profuse_file.cpp Normal file
View File

@ -0,0 +1,136 @@
/*
* profuse_file.cpp
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#include "profuse.h"
#include <cerrno>
#include <cstdio>
#pragma mark Read Functions
void prodos_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
fprintf(stderr, "open: %u\n", (unsigned)ino);
uint8_t buffer[BLOCK_SIZE];
int ok;
FileEntry *e = NULL;
ERROR(ino == 1, EISDIR)
ok = disk->Read(ino >> 9, buffer);
ERROR(ok < 0, EIO)
e = new FileEntry();
e->Load(buffer + (ino & 0x1ff));
if (e->storage_type == EXTENDED_FILE)
{
ok = disk->Normalize(*e, 0);
if (ok < 0)
{
delete e;
ERROR(true, EIO)
}
}
// EXTENDED_FILE already handled (it would be an error here.)
switch(e->storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
break;
//case PASCAL_FILE: //?
case DIRECTORY_FILE:
delete e;
ERROR(true, EISDIR)
break;
default:
ERROR(true, EIO)
}
if ( (fi->flags & O_ACCMODE) != O_RDONLY)
{
delete e;
ERROR(true, EACCES);
}
fi->fh = (uint64_t)e;
fuse_reply_open(req, fi);
}
void prodos_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
fprintf(stderr, "release: %u\n", (unsigned)ino);
FileEntry *e = (FileEntry *)fi->fh;
if (e) delete e;
fuse_reply_err(req, 0);
}
void prodos_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
fprintf(stderr, "read: %u %u %u\n", (unsigned)ino, (unsigned)size, (unsigned)off);
FileEntry *e = (FileEntry *)fi->fh;
ERROR(e == NULL, EIO)
if (off >= e->eof)
{
fuse_reply_buf(req, NULL, 0);
return;
}
unsigned level = 0;
switch(e->storage_type)
{
case TREE_FILE:
level = 2;
break;
case SAPLING_FILE:
level = 1;
break;
case SEEDLING_FILE:
level = 0;
break;
}
// currently, reading is done on a block basis.
// experimentally, fuse reads the entire file
// this may not hold for larger files.
// TODO -- error if size + off > eof.
unsigned blocks = (size + (off & 0x1ff) + BLOCK_SIZE - 1) >> 9;
int ok;
uint8_t *buffer = new uint8_t[blocks << 9];
fprintf(stderr, "ReadIndex(%x, buffer, %x, %x, %x)\n", e->key_pointer, level, (int)off, (int)blocks);
ok = disk->ReadIndex(e->key_pointer, buffer, level, off, blocks);
if (ok < 0)
{
fuse_reply_err(req, EIO);
}
else
{
fuse_reply_buf(req, (const char *)buffer + (off & 0x1ff), size);
}
delete []buffer;
}

253
bin/profuse_stat.cpp Normal file
View File

@ -0,0 +1,253 @@
/*
* prodos_stat.cpp
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#pragma mark Stat Functions
#include "profuse.h"
#include <cerrno>
#include <cstring>
#include <cstdio>
#include <vector>
#include <sys/stat.h>
#include <sys/statvfs.h>
using std::vector;
int prodos_stat(FileEntry& e, struct stat *st)
{
uint8_t buffer[BLOCK_SIZE];
int ok;
if (e.storage_type == EXTENDED_FILE)
{
ok = disk->Normalize(e, 0);
if (ok < 0) return ok;
}
st->st_blksize = BLOCK_SIZE;
st->st_ctime = e.creation;
#ifdef HAVE_STAT_BIRTHTIME
st->st_birthtime = e.creation;
#endif
st->st_mtime = e.last_mod;
st->st_atime = e.last_mod;
st->st_nlink = 1;
st->st_mode = 0444 | S_IFREG;
st->st_size = e.eof;
if (e.storage_type == DIRECTORY_FILE)
{
ok = disk->Read(e.key_pointer, buffer);
if (ok < 0) return -1;
SubdirEntry se;
se.Load(buffer + 0x04);
if (se.storage_type != SUBDIR_HEADER) return -1;
st->st_mode = S_IFDIR | 0555;
st->st_size = BLOCK_SIZE;
st->st_nlink = se.file_count + 1;
return 0;
}
switch(e.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
//case PASCAL_FILE:
break;
default:
return -1;
}
return 0;
}
int prodos_stat(const VolumeEntry &v, struct stat *st)
{
if (v.storage_type != VOLUME_HEADER) return -1;
st->st_mode = S_IFDIR | 0555;
st->st_ctime = v.creation;
#ifdef HAVE_STAT_BIRTHTIME
st->st_birthtime = v.creation;
#endif
st->st_mtime = v.last_mod;
st->st_atime = v.last_mod;
st->st_nlink = v.file_count + 1;
st->st_size = BLOCK_SIZE;
st->st_blksize = BLOCK_SIZE;
return 1;
}
void prodos_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
uint8_t buffer[BLOCK_SIZE];
struct stat st;
int ok;
fprintf(stderr, "get_attr %u\n", (unsigned)ino);
bzero(&st, sizeof(st));
/*
* ino 1 is the volume header. Others are pointers.
*
*/
ok = disk->Read(ino == 1 ? 2 : ino >> 9, buffer);
ERROR(ok < 0, EIO)
// ino 1 is the volume header.
if (ino == 1)
{
VolumeEntry v;
v.Load(buffer + 0x04);
ok = prodos_stat(v, &st);
ERROR(ok < 0, EIO);
st.st_ino = ino;
fuse_reply_attr(req, &st, 0.0);
return;
}
else
{
FileEntry e;
e.Load(buffer + (ino & 0x1ff));
ok = prodos_stat(e, &st);
ERROR(ok < 0, EIO);
st.st_ino = ino;
fuse_reply_attr(req, &st, 0.0); //
}
}
// TODO -- add Disk::Lookup support so we don't have to parse the entire dir header.
// TODO -- add caching.
void prodos_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
uint8_t buffer[BLOCK_SIZE];
struct fuse_entry_param entry;
int ok;
vector<FileEntry> files;
fprintf(stderr, "lookup: %u %s\n", (unsigned)parent, name);
ERROR(!validProdosName(name), ENOENT)
ok = disk->Read(parent == 1 ? 2 : parent >> 9, buffer);
ERROR(ok < 0, EIO)
bzero(&entry, sizeof(entry));
entry.attr_timeout = 0.0;
entry.entry_timeout = 0.0;
// get the file list
// TODO -- Disk::look-up-one-file
if (parent == 1)
{
VolumeEntry v;
ok = disk->ReadVolume(&v, &files);
ERROR(ok < 0, EIO)
}
else
{
FileEntry e;
e.Load(buffer + (parent & 0x1ff));
ERROR(e.storage_type != DIRECTORY_FILE, ENOENT);
ok = disk->ReadDirectory(e.key_pointer, NULL, &files);
ERROR(ok < 0, EIO)
}
// ok, now go through the file list and look for a (case insensitive) match.
ok = -1;
unsigned name_length = strlen(name);
for(vector<FileEntry>::iterator iter = files.begin(); iter != files.end(); ++iter)
{
FileEntry& f = *iter;
if ( (f.name_length == name_length) && (strcasecmp(name, f.file_name) == 0))
{
ok = prodos_stat(f, &entry.attr);
fprintf(stderr, "stat %s %x (%x %x) %d\n", f.file_name, f.address, f.address >> 9, f.address & 0x1ff, ok);
entry.ino = f.address;
entry.attr.st_ino = f.address;
break;
}
}
ERROR(ok < 0, ENOENT);
fprintf(stderr, "file found!\n");
fuse_reply_entry(req, &entry);
}
void prodos_statfs(fuse_req_t req, fuse_ino_t ino)
{
struct statvfs vst;
VolumeEntry volume;
disk->ReadVolume(&volume, NULL);
// returns statvfs for the mount path or any file in the fs
// therefore, ignore ino.
std::memset(&vst, 0, sizeof(vst));
vst.f_bsize = 512; // fs block size
vst.f_frsize = 512; // fundamental fs block size
vst.f_blocks = volume.total_blocks;
vst.f_bfree = 0; // free blocks
vst.f_bavail = 0; // free blocks (non-root)
vst.f_files = 0; // ?
vst.f_ffree = -1; // free inodes.
vst.f_favail = -1; // free inodes (non-root)
vst.f_fsid = 0; // file system id?
vst.f_flag = ST_RDONLY | ST_NOSUID;
vst.f_namemax = 15;
fuse_reply_statfs(req, &vst);
}

575
bin/profuse_xattr.cpp Normal file
View File

@ -0,0 +1,575 @@
/*
* profuse_xattr.cpp
* profuse
*
* Created by Kelvin Sherlock on 1/23/2009.
*
*/
#include "profuse.h"
#include <algorithm>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdio>
#include <strings.h>
using std::string;
#ifdef __APPLE__
#define NO_ATTRIBUTE ENOATTR
#else
#define NO_ATTRIBUTE EOPNOTSUPP
#endif
static bool isTextFile(unsigned ftype, unsigned auxtype)
{
if (ftype == 0x04) return true; // ascii text
if (ftype == 0xb0) return true; // source code.
if (ftype == 0x50 && auxtype == 0x5445) return true; // teach text
return false;
}
static const char *mimeType(unsigned ftype, unsigned auxtype)
{
switch(ftype)
{
case 0x04:
if (auxtype == 0) return "text/plain";
break;
case 0xb0:
return "text/plain";
break;
case 0x50:
if (auxtype == 0x5445) return "text/plain";
break;
case 0xc0:
if (auxtype == 0x8006) return "image/gif";
break;
case 0xe0:
if (auxtype == 0x8000) return "application/x-BinaryII";
if (auxtype == 0x8002) return "application/x-Shrinkit";
break;
}
return NULL;
}
static void setCreator(uint8_t *finfo, unsigned ftype, unsigned auxtype)
{
/*
tech note PT515
ProDOS -> Macintosh conversion
ProDOS Macintosh
Filetype Auxtype Creator Filetype
$00 $0000 'pdos' 'BINA'
$B0 (SRC) (any) 'pdos' 'TEXT'
$04 (TXT) $0000 'pdos' 'TEXT'
$FF (SYS) (any) 'pdos' 'PSYS'
$B3 (S16) (any) 'pdos' 'PS16'
$uv $wxyz 'pdos' 'p' $uv $wx $yz
Programmer's Reference for System 6.0:
ProDOS Macintosh
File Type Auxiliary Type Creator Type File Type
$00 $0000 pdos BINA
$04 (TXT) $0000 pdos TEXT
$FF (SYS) (any) pdos PSYS
$B3 (S16) $DByz pdos p $B3 $DB $yz
$B3 (S16) (any) pdos PS16
$D7 $0000 pdos MIDI
$D8 $0000 pdos AIFF
$D8 $0001 pdos AIFC
$E0 $0005 dCpy dImg
$FF (SYS) (any) pdos PSYS
$uv $wxyz pdos p $uv $wx $yz
*/
finfo[0] = 'p';
finfo[1] = ftype;
finfo[2] = auxtype >> 8;
finfo[3] = auxtype;
memcpy(finfo + 4, "pdos", 4);
switch (ftype)
{
case 0x00:
if (auxtype == 0) memcpy(finfo, "BINA", 4);
break;
case 0x04:
if (auxtype == 0) memcpy(finfo, "TEXT", 4);
break;
case 0x50:
if (auxtype == 0x5445) memcpy(finfo, "TEXT", 4);
break;
case 0xb0:
memcpy(finfo, "TEXT", 4);
break;
case 0xb3:
if ((auxtype >> 8) != 0xdb) memcpy(finfo, "PS16", 4);
break;
case 0xd7:
if (auxtype == 0) memcpy(finfo, "MIDI", 4);
break;
case 0xd8:
if (auxtype == 0) memcpy(finfo, "AIFF", 4);
if (auxtype == 1) memcpy(finfo, "AIFC", 4);
break;
case 0xe0:
if (auxtype == 5) memcpy(finfo, "dImgdCpy", 8);
break;
case 0xff:
memcpy(finfo, "PSYS", 4);
break;
}
}
#pragma mark XAttribute Functions
static void xattr_filetype(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
uint8_t attr = e.file_type;
unsigned attr_size = 1;
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
// consider position here?
fuse_reply_buf(req, (char *)&attr, attr_size);
}
static void xattr_auxtype(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
uint8_t attr[2];
unsigned attr_size = 2;
attr[0] = e.aux_type & 0xff;
attr[1] = (e.aux_type >> 8) & 0xff;
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
// consider position here?
fuse_reply_buf(req, (char *)&attr, attr_size);
}
// user.charset
static void xattr_charset(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
const char attr[] = "macintosh";
unsigned attr_size = sizeof(attr) - 1;
ERROR(!isTextFile(e.file_type, e.aux_type), NO_ATTRIBUTE)
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
fuse_reply_buf(req, (char *)&attr, attr_size);
}
//apple.TextEncoding
static void xattr_textencoding(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
const char attr[] = "MACINTOSH;0";
unsigned attr_size = sizeof(attr) - 1;
ERROR(!isTextFile(e.file_type, e.aux_type), NO_ATTRIBUTE)
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
fuse_reply_buf(req, (char *)&attr, attr_size);
}
static void xattr_rfork(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
int ok;
unsigned level;
ERROR (e.storage_type != EXTENDED_FILE, NO_ATTRIBUTE)
ok = disk->Normalize(e, 1);
ERROR(ok < 0, EIO)
switch(e.storage_type)
{
case SEEDLING_FILE:
level = 0;
break;
case SAPLING_FILE:
level = 1;
break;
case TREE_FILE:
level = 2;
break;
default:
ERROR(true, EIO)
}
if (size == 0)
{
fuse_reply_xattr(req, e.eof);
return;
}
size = std::min((uint32_t)(size + off), e.eof);
unsigned blocks = (size + (off & 0x1ff) + BLOCK_SIZE - 1) >> 9;
uint8_t *buffer = new uint8_t[blocks << 9];
fprintf(stderr, "ReadIndex(%x, buffer, %x, %x, %x)\n", e.key_pointer, level, (int)off, (int)blocks);
ok = disk->ReadIndex(e.key_pointer, buffer, level, off, blocks);
if (ok < 0)
{
fuse_reply_err(req, EIO);
}
else
{
fuse_reply_buf(req, (char *)buffer + (off & 0x1ff), size);
}
delete []buffer;
return;
}
// Finder info.
static void xattr_finfo(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
int ok;
ExtendedEntry ee;
uint8_t attr[32];
unsigned attr_size = 32;
//ERROR (e.storage_type != EXTENDED_FILE, ENOENT)
switch (e.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
bzero(attr, attr_size);
setCreator(attr, e.file_type, e.aux_type);
fuse_reply_buf(req, (char *)attr, attr_size);
return;
case EXTENDED_FILE:
// handled below.
break;
default:
ERROR(true, NO_ATTRIBUTE);
}
ok = disk->Normalize(e, 1, &ee);
ERROR(ok < 0, EIO)
// sanity check
switch(e.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
break;
default:
ERROR(true, EIO)
}
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
memcpy(attr, ee.FInfo, 16);
memcpy(attr + 16, ee.xFInfo, 16);
// if no creator, create one.
if (memcmp(attr, "\0\0\0\0\0\0\0\0", 8) == 0)
setCreator(attr, e.file_type, e.aux_type);
fuse_reply_buf(req, (char *)attr, attr_size);
}
static void xattr_mimetype(FileEntry& e, fuse_req_t req, size_t size, off_t off)
{
unsigned attr_size;
const char *mime = mimeType(e.file_type, e.aux_type);
ERROR(!mime, NO_ATTRIBUTE);
attr_size = strlen(mime);
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
ERROR (size < attr_size, ERANGE)
fuse_reply_buf(req, mime, attr_size);
}
void prodos_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
// list of supported attributes.
//
#define NO_ATTR() \
{ \
if (size) fuse_reply_buf(req, NULL, 0); \
else fuse_reply_xattr(req, 0); \
return; \
}
fprintf(stderr, "listxattr %u\n", (unsigned)ino);
uint8_t buffer[BLOCK_SIZE];
int ok;
unsigned attr_size;
string attr;
if(ino == 1)
NO_ATTR()
ok = disk->Read(ino >> 9, buffer);
ERROR(ok < 0, EIO)
FileEntry e;
e.Load(buffer + (ino & 0x1ff));
attr += "prodos.FileType";
attr.append(1, 0);
attr += "prodos.AuxType";
attr.append(1, 0);
switch(e.storage_type)
{
case EXTENDED_FILE:
{
// TODO -- pretend there's no resource fork if resource fork eof == 0 ?
//
//ok = disk->Normalize(e, 1);
//ERROR(ok < 0, EIO)
attr += "prodos.ResourceFork";
attr.append(1, 0);
attr += "com.apple.FinderInfo";
attr.append(1, 0);
break;
}
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
// generate HFS creator codes.
attr += "com.apple.FinderInfo";
attr.append(1, 0);
break;
case DIRECTORY_FILE:
NO_ATTR()
break;
default:
NO_ATTR()
break;
}
if (isTextFile(e.file_type, e.aux_type))
{
attr += "com.apple.TextEncoding";
attr.append(1, 0);
attr += "user.charset";
attr.append(1, 0);
}
if (mimeType(e.file_type, e.aux_type))
{
attr += "user.mime_type";
attr.append(1, 0);
}
attr_size = attr.length();
fprintf(stderr, "%d %s\n", attr_size, attr.c_str());
if (size == 0)
{
fuse_reply_xattr(req, attr_size);
return;
}
if (size < attr_size)
{
fuse_reply_err(req, ERANGE);
return;
}
fuse_reply_buf(req, attr.data(), attr_size);
return;
}
/*
* offset is only valid in OS X for the resource fork.
*
*/
void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t off)
{
fprintf(stderr, "getxattr: %u %s %u %u \n", (unsigned)ino, name, (unsigned)size, (unsigned)off);
uint8_t buffer[BLOCK_SIZE];
ERROR(ino == 1, NO_ATTRIBUTE) // finder can't handle EISDIR.
int ok = disk->Read(ino >> 9, buffer);
ERROR(ok < 0, EIO)
FileEntry e;
e.Load(buffer + (ino & 0x1ff));
switch(e.storage_type)
{
case SEEDLING_FILE:
case SAPLING_FILE:
case TREE_FILE:
case EXTENDED_FILE:
break;
case DIRECTORY_FILE:
ERROR(true, NO_ATTRIBUTE) // Finder can't handle EISDIR.
default:
ERROR(true, NO_ATTRIBUTE);
}
if (strcmp("prodos.FileType", name) == 0)
{
xattr_filetype(e, req, size, off);
return;
}
if (strcmp("prodos.AuxType", name) == 0)
{
xattr_auxtype(e, req, size, off);
return;
}
if (strcmp("com.apple.TextEncoding", name) == 0)
{
xattr_textencoding(e, req, size, off);
return;
}
if ( (e.storage_type == EXTENDED_FILE) && (strcmp("prodos.ResourceFork", name) == 0))
{
xattr_rfork(e, req, size, off);
return;
}
if ( strcmp("com.apple.FinderInfo", name) == 0)
{
xattr_finfo(e, req, size, off);
return;
}
// linux standard
if (strcmp("user.mime_type", name) == 0)
{
xattr_mimetype(e, req, size, off);
return;
}
// linux standard
if (strcmp("user.charset", name) == 0)
{
xattr_charset(e, req, size, off);
return;
}
fuse_reply_err(req, NO_ATTRIBUTE);
}
/*
* Linux, et alia do not have an offset parameter.
*/
void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
{
prodos_getxattr(req, ino, name, size, 0);
}

552
bin/xattr.cpp Normal file
View File

@ -0,0 +1,552 @@
#include <string>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cerrno>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <err.h>
typedef std::vector<std::string>::iterator vsiter;
// prototypes to shut up clang.
void hexdump(const uint8_t *data, ssize_t size);
ssize_t get_attr_list(const char * fname, std::vector<std::string> &out);
void dumpxattr(const char *file, const char *attr);
int op_list(int argc, char **argv);
int op_dump(int argc, char **argv);
int op_read(int argc, char **argv);
int op_rm(int argc, char **argv);
int op_mv(int argc, char **argv);
int op_cp(int argc, char **argv);
ssize_t read_all(int fd, std::vector<uint8_t> &out);
int op_write(int argc, char **argv);
void usage(const char *name);
#ifdef __APPLE__
// apple has additional parameter for position and options.
inline ssize_t getxattr(const char *path, const char *name, void *value, size_t size)
{
return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
}
// apple has additional parameter for options.
inline ssize_t listxattr(const char *path, char *namebuf, size_t size)
{
return listxattr(path, namebuf, size, XATTR_NOFOLLOW);
}
// apple has additional parameter for offset .
inline int setxattr(const char *path, const char *name, void *value, size_t size, int flags)
{
return setxattr(path, name, value, size, 0, flags);
}
inline int removexattr(const char *path, const char *name) {
return removexattr(path, name, 0);
}
#endif
void hexdump(const uint8_t *data, ssize_t size)
{
const char *HexMap = "0123456789abcdef";
char buffer1[16 * 3 + 1 + 1];
char buffer2[16 + 1];
ssize_t offset = 0;
unsigned i, j;
while(size > 0)
{
std::memset(buffer1, ' ', sizeof(buffer1));
std::memset(buffer2, ' ', sizeof(buffer2));
unsigned linelen = (unsigned)std::min(size, (ssize_t)16);
for (i = 0, j = 0; i < linelen; i++)
{
unsigned x = data[i];
buffer1[j++] = HexMap[x >> 4];
buffer1[j++] = HexMap[x & 0x0f];
j++;
if (i == 7) j++;
// isascii not part of std:: and may be a macro.
buffer2[i] = isascii(x) && std::isprint(x) ? x : '.';
}
buffer1[sizeof(buffer1)-1] = 0;
buffer2[sizeof(buffer2)-1] = 0;
std::printf("%06x:\t%s\t%s\n", (unsigned)offset, buffer1, buffer2);
offset += 16;
data += 16;
size -= 16;
}
std::printf("\n");
}
/*
* build a list of attribute names for a file.
* returns -1 on failure
* otherwise, returns # of attributes.
*
*/
ssize_t get_attr_list(const char * fname, std::vector<std::string> &out)
{
ssize_t asize = listxattr(fname, NULL, 0);
if (asize < 0) return -1;
if (asize == 0) return 0;
char *buffer = new char[asize];
if ((asize = listxattr(fname, buffer, asize)) < 0)
{
delete []buffer;
return -1;
}
//hexdump((uint8_t *)buffer, asize);
char *cp = buffer;
// buffer is null-terminated utf-8 strings
// eg:
// c a t 0 d o g 0
// 0 1 2 3 4 5 6 7
while (asize > 0)
{
ssize_t len = std::strlen(cp);
out.push_back(std::string(cp, len));
++len;
asize -= len;
cp += len;
}
delete []buffer;
return out.size();
}
/*
* safely read the entire xattr into buffer.
*
*/
int read_xattr(const char *fname, const char *attr_name, std::vector<uint8_t> &buffer) {
for(;;) {
ssize_t asize = getxattr(fname, attr_name, NULL, 0);
if (asize < 0) {
if (errno == EINTR) continue;
return -1;
}
buffer.resize(asize);
asize = getxattr(fname, attr_name, buffer.data(), asize);
if (asize < 0) {
// ERANGE -- buffer is not large enough.
if (errno == EINTR || errno == ERANGE) continue;
return -1;
}
buffer.resize(asize);
return 0;
}
}
/*
* hexdump an individual attribute.
*/
void dumpxattr(const char *fname, const char *attr_name)
{
std::vector<uint8_t> buffer;
if (read_xattr(fname, attr_name, buffer) < 0) {
warn("getxattr(\"%s\", \"%s\")", fname, attr_name);
return;
}
std::printf("%s: %u\n", attr_name, (unsigned)buffer.size());
hexdump(buffer.data(), buffer.size());
}
/*
* list a file's attributes (and size)
*
*/
int op_list(int argc, char **argv)
{
const char *fname = *argv;
std::vector<std::string>attr;
if (argc == 1)
{
get_attr_list(fname, attr);
std::sort(attr.begin(), attr.end());
}
else
{
for (int i = 1; i < argc; ++i)
{
attr.push_back(argv[i]);
}
}
// find the max name length (not utf-8 happy)
unsigned maxname = 0;
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
{
maxname = std::max(maxname, (unsigned)i->length());
}
maxname += 2;
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
{
const char *attr_name = i->c_str();
ssize_t asize = getxattr(fname, attr_name, NULL, 0);
if (asize >= 0)
{
std::printf("%-*s %8u\n", maxname, attr_name, (unsigned)asize);
}
else
{
warn("getxattr(\"%s\", \"%s\")", fname, attr_name);
}
}
return 0;
}
/*
* hexdump a file's attributes.
*
*/
int op_dump(int argc, char **argv)
{
const char *fname = *argv;
std::vector<std::string>attr;
if (argc == 1)
{
get_attr_list(fname, attr);
std::sort(attr.begin(), attr.end());
}
else
{
for (int i = 1; i < argc; ++i)
{
attr.push_back(argv[i]);
}
}
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
{
const char *attr_name = i->c_str();
dumpxattr(fname, attr_name);
}
return 0;
}
// must specify the xattr name.
int op_read(int argc, char **argv)
{
if (argc != 2)
{
std::fprintf(stderr, "Must specify attribute to be read.\n");
return -1;
}
const char *fname = argv[0];
const char *attr_name = argv[1];
std::vector<uint8_t> buffer;
if (read_xattr(fname, attr_name, buffer) < 0 ) {
warn("getxattr(\"%s\", \"%s\")", fname, attr_name);
return -1;
}
if (buffer.size() == 0) return 0;
if (::write(STDOUT_FILENO, buffer.data(), buffer.size()) < 0)
{
warn("write");
return -1;
}
return 0;
}
ssize_t read_all(int fd, std::vector<uint8_t> &out)
{
struct stat st;
ssize_t bsize;
// stdin could be a file, a pipe, or a tty.
if (fstat(fd, &st) < 0)
{
warn("fstat stdin");
return -1;
}
bsize = std::max(st.st_blksize, (blksize_t)512);
out.reserve(std::max(st.st_size, (off_t)512));
std::vector<uint8_t> buffer;
buffer.resize(bsize);
for(;;)
{
ssize_t size = ::read(fd, buffer.data(), bsize);
if (size == 0) break;
if (size == -1)
{
if (errno == EINTR) continue;
warn("read");
return -1;
}
// force reserve?
out.insert(out.end(), buffer.data(), buffer.data() + size);
}
return out.size();
}
// xattr write filename attrname
// stdin -> filename:attrname
int op_write(int argc, char **argv)
{
std::vector<uint8_t> buffer;
if (argc != 2)
{
std::fprintf(stderr, "Must specify attribute to be written.\n");
return -1;
}
const char *fname = argv[0];
const char *attr_name = argv[1];
if (read_all(STDIN_FILENO, buffer) < 0)
{
return -1;
}
// Flag options:
// XATTR_CREATE : fails if attr already exists.
// XATTR_REPLACE : fails if attr does not exist.
// if neither of the above is specified, will create and replace.
// XATTR_NOFOLLOW : do not follow symbolic links (Apple only)
ssize_t asize = setxattr(fname, attr_name, buffer.data(), buffer.size(), 0);
if (asize < 0)
{
warn("setxattr(\"%s\", \"%s\")", fname, attr_name);
return -1;
}
return 0;
}
/*
* remove an attribute
*
*/
int op_rm(int argc, char **argv)
{
int rv = 0;
const char *fname = *argv;
std::vector<std::string>attr;
if (argc == 1)
{
std::fprintf(stderr, "Must specify attribute to be removed.\n");
return -1;
}
for (int i = 1; i < argc; ++i)
{
attr.push_back(argv[i]);
}
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
{
const char *attr_name = i->c_str();
int ok = removexattr(fname, attr_name);
if (ok < 0) {
warn("removexattr(\"%s\", \"%s\")", fname, attr_name);
rv = 1;
}
}
return rv;
}
/*
* copy an attribute
*
*/
int op_cp(int argc, char **argv)
{
const char *fname = *argv;
std::vector<std::string>attr;
if (argc != 3)
{
std::fprintf(stderr, "Must specify source and destination.\n");
return -1;
}
const char *src_attr_name = argv[1];
const char *dest_attr_name = argv[2];
std::vector<uint8_t> buffer;
if (read_xattr(fname, src_attr_name, buffer) < 0) {
warn("getxattr(\"%s\", \"%s\")", fname, src_attr_name);
return -1;
}
ssize_t asize = setxattr(fname, dest_attr_name, buffer.data(), buffer.size(), 0);
if (asize < 0)
{
warn("setxattr(\"%s\", \"%s\")", fname, dest_attr_name);
return -1;
}
return 0;
}
/*
* move an attribute
*
*/
int op_mv(int argc, char **argv)
{
const char *fname = *argv;
std::vector<std::string>attr;
if (argc != 3)
{
std::fprintf(stderr, "Must specify source and destination.\n");
return -1;
}
const char *src_attr_name = argv[1];
const char *dest_attr_name = argv[2];
std::vector<uint8_t> buffer;
if (read_xattr(fname, src_attr_name, buffer) < 0) {
warn("getxattr(\"%s\", \"%s\")", fname, src_attr_name);
return -1;
}
ssize_t asize = setxattr(fname, dest_attr_name, buffer.data(), buffer.size(), 0);
if (asize < 0)
{
warn("setxattr(\"%s\", \"%s\")", fname, dest_attr_name);
return -1;
}
int ok = removexattr(fname, src_attr_name);
if (ok < 0) {
warn("removexattr(\"%s\", \"%s\")", fname, dest_attr_name);
return -1;
}
return 0;
}
void usage(const char *name)
{
std::printf("usage:\n");
std::printf("%s list file [attr ...]\n", name);
std::printf("%s dump file [attr ...]\n", name);
std::printf("%s read file attr\n", name);
std::printf("%s write file attr\n", name);
std::exit(0);
}
/*
* xattr list file [xattr ...]
* xattr dump file [xattr ...]
*
*/
int main(int argc, char **argv)
{
if (argc < 3) usage(*argv);
if (std::strcmp(argv[1], "list") == 0 || std::strcmp(argv[1], "ls") == 0)
return op_list(argc - 2, argv + 2);
if (std::strcmp(argv[1], "dump") == 0 || std::strcmp(argv[1], "hd") == 0)
return op_dump(argc - 2, argv + 2);
if (std::strcmp(argv[1], "read") == 0) return op_read(argc - 2, argv + 2);
if (std::strcmp(argv[1], "write") == 0) return op_write(argc - 2, argv + 2);
if (std::strcmp(argv[1], "rm") == 0) return op_rm(argc - 2, argv + 2);
if (std::strcmp(argv[1], "mv") == 0) return op_mv(argc - 2, argv + 2);
if (std::strcmp(argv[1], "cp") == 0) return op_cp(argc - 2, argv + 2);
usage(*argv);
return 1;
}

Some files were not shown because too many files have changed in this diff Show More