Compare commits

...

138 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
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
88 changed files with 9559 additions and 2974 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

@ -11,30 +11,25 @@
#include <Cache/BlockCache.h>
#include <Device/BlockDevice.h>
#include <ProFUSE/Exception.h>
#include <ProFUSE/auto.h>
#include <Common/Exception.h>
#include <Common/auto.h>
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
#pragma mark -
#pragma mark BlockCache
BlockCache::BlockCache(BlockDevice *device)
BlockCache::BlockCache(BlockDevicePointer device) :
_device(device)
{
_device = device;
_blocks = device->blocks();
_readOnly = device->readOnly();
}
BlockCache::~BlockCache()
{
delete _device;
}
void BlockCache::write(unsigned block, const void *bp)
@ -52,9 +47,10 @@ void BlockCache::read(unsigned block, void *bp)
}
BlockCache *BlockCache::Create(BlockDevice *device)
BlockCachePointer BlockCache::Create(BlockDevicePointer device)
{
if (!device) return NULL;
// this just calls the device virtual function to create a cache.
if (!device) return BlockCachePointer();
return device->createBlockCache();
}

View File

@ -3,12 +3,13 @@
#include <stdint.h>
#include <vector>
#include <Device/Device.h>
class MappedFile;
namespace Device {
class BlockDevice;
enum BlockReleaseFlags {
kBlockDirty = 1,
@ -19,13 +20,13 @@ enum BlockReleaseFlags {
class BlockCache {
public:
static BlockCache *Create(BlockDevice *device);
static BlockCachePointer Create(BlockDevicePointer device);
virtual ~BlockCache();
bool readOnly() { return _readOnly; }
unsigned blocks() { return _blocks; }
BlockDevice *device() { return _device; }
BlockDevicePointer device() { return _device; }
virtual void sync() = 0;
@ -46,16 +47,16 @@ public:
}
protected:
BlockCache(BlockDevice *device);
BlockCache(BlockDevicePointer device);
BlockDevice *_device;
BlockDevicePointer _device;
private:
unsigned _blocks;
bool _readOnly;
};
} // namespace
#endif

View File

@ -10,8 +10,8 @@
#include <Device/BlockDevice.h>
#include <Cache/ConcreteBlockCache.h>
#include <ProFUSE/Exception.h>
#include <ProFUSE/auto.h>
#include <Common/Exception.h>
#include <Common/auto.h>
/*
@ -51,15 +51,20 @@
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
typedef std::vector<ConcreteBlockCache::Entry *>::iterator EntryIter;
//typedef std::vector<ConcreteBlockCache::Entry *>::iterator EntryIter;
BlockCachePointer ConcreteBlockCache::Create(BlockDevicePointer device, unsigned size)
{
//return BlockCachePointer(new ConcreteBlockCache(device, size));
// constructor must be accessible to std::make_shared...
return MAKE_SHARED(ConcreteBlockCache, device, size);
}
ConcreteBlockCache::ConcreteBlockCache(BlockDevice *device, unsigned size) :
ConcreteBlockCache::ConcreteBlockCache(BlockDevicePointer device, unsigned size) :
BlockCache(device)
{
if (size < 16) size = 16;

View File

@ -9,7 +9,9 @@ namespace Device {
class ConcreteBlockCache : public BlockCache {
public:
ConcreteBlockCache(BlockDevice *device, unsigned size = 16);
static BlockCachePointer Create(BlockDevicePointer device, unsigned size = 16);
virtual ~ConcreteBlockCache();
virtual void sync();
@ -20,9 +22,14 @@ public:
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;
@ -36,6 +43,8 @@ private:
};
typedef std::vector<Entry *>::iterator EntryIter;
enum { HashTableSize = 23 };
std::vector<Entry *>_buffers;

View File

@ -2,6 +2,7 @@
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <cstddef>
#include <sys/types.h>
#include <sys/mman.h>
@ -10,17 +11,21 @@
#include <Cache/MappedBlockCache.h>
#include <Device/BlockDevice.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
#include <POSIX/Exception.h>
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
BlockCachePointer MappedBlockCache::Create(BlockDevicePointer device, void *data)
{
//return BlockCachePointer(new MappedBlockCache(device, data));
return MAKE_SHARED(MappedBlockCache, device, data);
}
MappedBlockCache::MappedBlockCache(BlockDevice *device, void *data) :
MappedBlockCache::MappedBlockCache(BlockDevicePointer device, void *data) :
BlockCache(device)
{
_data = (uint8_t *)data;
@ -118,12 +123,12 @@ void MappedBlockCache::sync(unsigned block)
end = (void *)((ptrdiff_t)end / pagesize * pagesize);
if (::msync(start, pagesize, MS_SYNC) != 0)
throw POSIXException(__METHOD__ ": msync", errno);
throw POSIX::Exception(__METHOD__ ": msync", errno);
if (start != end)
{
if (::msync(end, pagesize, MS_SYNC) != 0)
throw POSIXException(__METHOD__ ": msync", errno);
throw POSIX::Exception(__METHOD__ ": msync", errno);
}
}

View File

@ -8,7 +8,8 @@ namespace Device {
class MappedBlockCache : public BlockCache {
public:
MappedBlockCache(BlockDevice *, void *data);
static BlockCachePointer Create(BlockDevicePointer device, void *data);
virtual ~MappedBlockCache();
virtual void sync();
@ -21,8 +22,12 @@ class MappedBlockCache : public BlockCache {
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;

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()
{

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

@ -2,9 +2,10 @@
#include <cstring>
#include <cstdio>
#include <Device/Adaptor.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
using namespace Device;
@ -141,7 +142,7 @@ uint8_t NibbleAdaptor::encode62(uint8_t val)
};
if (val > 0x3f)
throw ProFUSE::Exception(__METHOD__ ": Invalid 6-2 value.");
throw ::Exception(__METHOD__ ": Invalid 6-2 value.");
return table[val];
}
@ -164,7 +165,7 @@ uint8_t NibbleAdaptor::decode62(uint8_t val)
};
if ((val < 0x90) || (table[val - 0x90] == 0xff))
throw ProFUSE::Exception(__METHOD__ ": Invalid 6-2 encoding.");
throw ::Exception(__METHOD__ ": Invalid 6-2 encoding.");
return table[val - 0x90];
}
@ -237,12 +238,13 @@ NibbleAdaptor::NibbleAdaptor(void *address, unsigned length)
checksum = decode44(buffer[offset + 9], buffer[offset + 10]);
if (volume ^ track ^ sector ^ checksum)
throw ProFUSE::Exception(__METHOD__ ": Invalid address checksum.");
throw ::Exception(__METHOD__ ": Invalid address checksum.");
if (track > 35 || sector > 16)
throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector.");
throw ::Exception(__METHOD__ ": Invalid track/sector.");
offset += 3 + 8 + 3;
state = 1;
continue;
@ -250,9 +252,15 @@ NibbleAdaptor::NibbleAdaptor(void *address, unsigned length)
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 += 3 + 342 + 1 + 3;
offset++;
state = 0;
continue;
}
@ -283,7 +291,11 @@ NibbleAdaptor::NibbleAdaptor(void *address, unsigned length)
for (std::vector<unsigned>::iterator iter = _index.begin(); iter != _index.end(); ++iter)
{
if (*iter == -1)
throw ProFUSE::Exception(__METHOD__ ": Sector missing.");
{
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.");
}
}
}
@ -296,22 +308,51 @@ void NibbleAdaptor::readBlock(unsigned block, void *bp)
{
unsigned track = (block & ~0x07) << 9;
unsigned sector = (block & 0x07) << 1;
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 sector = (block & 0x07) << 1;
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)
@ -320,7 +361,7 @@ void NibbleAdaptor::readTrackSector(TrackSector ts, void *bp)
#define __METHOD__ "NibbleAdaptor::readTrackSector"
if (ts.track > 35 || ts.sector > 16)
throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector.");
throw ::Exception(__METHOD__ ": Invalid track/sector.");
CircleBuffer buffer(_address, _length);
@ -331,6 +372,10 @@ void NibbleAdaptor::readTrackSector(TrackSector ts, void *bp)
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;
@ -373,7 +418,7 @@ void NibbleAdaptor::readTrackSector(TrackSector ts, void *bp)
if (checksum != decode62(buffer[index++]))
std::fprintf(stderr, "Invalid checksum on track %u, sector %u\n", ts.track, ts.sector);
//throw ProFUSE::Exception(__METHOD__ ": Invalid field checksum.");
//throw ::Exception(__METHOD__ ": Invalid field checksum.");
}
@ -383,7 +428,7 @@ void NibbleAdaptor::writeTrackSector(TrackSector ts, const void *bp)
#define __METHOD__ "NibbleAdaptor::writeTrackSector"
if (ts.track > 35 || ts.sector > 16)
throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector.");
throw ::Exception(__METHOD__ ": Invalid track/sector.");
uint8_t auxBuffer[86];
uint8_t checksum = 0;

View File

@ -12,7 +12,8 @@
#include <Device/BlockDevice.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>
@ -20,12 +21,40 @@
#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;
unsigned BlockDevice::ImageType(MappedFile *f, unsigned defv)
{
#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;
}
unsigned BlockDevice::ImageType(const char *type, unsigned defv)
{
@ -51,7 +80,9 @@ unsigned BlockDevice::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';
@ -70,15 +101,18 @@ unsigned BlockDevice::ImageType(const char *type, unsigned defv)
if (::strcasecmp(type, "davex") == 0)
return 'DVX_';
// not supported yet.
#ifdef HAVE_NUFX
if (::strcasecmp(type, "sdk") == 0)
return 'SDK_';
if (::strcasecmp(type, "shk") == 0)
return 'SDK_';
#endif
return defv;
}
BlockDevice *BlockDevice::Open(const char *name, bool readOnly, unsigned imageType)
BlockDevicePointer BlockDevice::Open(const char *name, File::FileFlags flags, unsigned imageType)
{
#undef __METHOD__
#define __METHOD__ "BlockDevice::Open"
@ -89,25 +123,22 @@ BlockDevice *BlockDevice::Open(const char *name, bool readOnly, unsigned imageTy
if (::stat(name, &st) != 0)
{
throw POSIXException(__METHOD__ ": stat error", errno);
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)
{
// /dev/xxxx
if (S_ISBLK(st.st_mode))
return RawDevice::Open(name, readOnly);
imageType = ImageType(name, 'PO__');
imageType = ImageType(&file, 'PO__');
}
// TODO -- if no image type, guess based on file size?
MappedFile file(name, readOnly);
switch (imageType)
{
case '2IMG':
@ -124,11 +155,17 @@ BlockDevice *BlockDevice::Open(const char *name, bool readOnly, unsigned imageTy
case 'DVX_':
return DavexDiskImage::Open(&file);
#if HAVE_NUFX
case 'SDK_':
return SDKImage::Open(name);
#endif
}
// throw an error?
return NULL;
return BlockDevicePointer();
}
@ -158,7 +195,7 @@ static std::string filename(const std::string& src)
}
BlockDevice *BlockDevice::Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType)
BlockDevicePointer BlockDevice::Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType)
{
std::string xname;
@ -191,7 +228,7 @@ BlockDevice *BlockDevice::Create(const char *fname, const char *vname, unsigned
return DavexDiskImage::Create(fname, blocks, vname);
}
return NULL;
return BlockDevicePointer();
}
@ -233,9 +270,9 @@ void BlockDevice::sync(TrackSector ts)
}
*/
BlockCache *BlockDevice::createBlockCache()
BlockCachePointer BlockDevice::createBlockCache()
{
unsigned b = blocks();
unsigned size = std::max(16u, b / 16);
return new ConcreteBlockCache(this, size);
return ConcreteBlockCache::Create(shared_from_this(), size);
}

View File

@ -4,23 +4,29 @@
#include <stdint.h>
#include <sys/types.h>
#include <ProFUSE/Exception.h>
#include <Device/Device.h>
#include <Device/TrackSector.h>
#include <Cache/BlockCache.h>
#include <Common/Exception.h>
#include <Common/smart_pointers.h>
#include <File/File.h>
class MappedFile;
namespace Device {
class BlockDevice {
class BlockDevice : public ENABLE_SHARED_FROM_THIS(BlockDevice) {
public:
// static methods.
static unsigned ImageType(const char *type, unsigned defv = 0);
static BlockDevice *Open(const char *name, bool readOnly, unsigned imageType = 0);
static BlockDevice *Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType = 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);
@ -28,7 +34,7 @@ public:
virtual ~BlockDevice();
virtual BlockCache *createBlockCache();
virtual BlockCachePointer createBlockCache();
virtual void read(unsigned block, void *bp) = 0;

View File

@ -19,8 +19,6 @@
using namespace Device;
using namespace LittleEndian;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
/*
http://www.umich.edu/~archive/apple2/technotes/ftn/FTN.E0.8004
@ -28,13 +26,6 @@ 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 *file) :
@ -54,58 +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->length();
const void * data = f->address();
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.");
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"
@ -115,7 +114,7 @@ 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->address();
@ -161,12 +160,15 @@ DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks, const ch
std::memcpy(file->address(), header.buffer(), 512);
file->sync();
return new DavexDiskImage(file);
//return BlockDevicePointer(new DavexDiskImage(file));
return MAKE_SHARED(DavexDiskImage, file);
}
BlockCache *DavexDiskImage::createBlockCache()
BlockCachePointer DavexDiskImage::createBlockCache()
{
return new MappedBlockCache(this, 512 + (uint8_t *)address());
// need a smart pointer, but only have this....
return MappedBlockCache::Create(shared_from_this(), 512 + (uint8_t *)address());
}

View File

@ -2,6 +2,7 @@
#define __DAVEXDISKIMAGE_H__
#include <string>
#include <new>
#include <Device/BlockDevice.h>
#include <Device/DiskImage.h>
@ -16,18 +17,19 @@ public:
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 BlockCache *createBlockCache();
virtual BlockCachePointer createBlockCache();
private:
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
DavexDiskImage(const char *, bool readOnly);
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

@ -16,9 +16,6 @@ using namespace Device;
using namespace BigEndian;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
enum {
@ -32,19 +29,11 @@ DiskCopy42Image::DiskCopy42Image(MappedFile *f) :
DiskImage(f),
_changed(false)
{
setAdaptor(new POAdaptor(oUserData + (uint8_t *)f->address()));
setBlocks(Read32(f->address(), oDataSize) / 512);
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()
{
@ -83,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)
@ -109,14 +100,14 @@ 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 + oUserData);
MappedFile *file = MappedFile::Create(name, blocks * 512 + oUserData);
@ -171,44 +162,43 @@ DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks, const
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();
bool ok = false;
uint32_t checksum = 0;
do {
if (size < oUserData) break;
// name must be < 64
if (Read8(data, 0) > 63) break;
if (Read32(data, oPrivate) != 0x100)
break;
// bytes, not blocks.
bytes = Read32(data, oDataSize);
if (bytes % 512) break;
if (size < oUserData + bytes) break;
// todo -- checksum.
checksum = Read32(data, oDataChecksum);
ok = true;
} while (false);
if (size < oUserData)
return false;
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
// 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);
@ -216,7 +206,21 @@ void DiskCopy42Image::Validate(MappedFile *file)
{
fprintf(stderr, __METHOD__ ": Warning: checksum invalid.\n");
}
return true;
}
bool DiskCopy42Image::Validate(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "DiskCopy42Image::Validate"
if (!Validate(file, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}
void DiskCopy42Image::write(unsigned block, const void *bp)
@ -226,11 +230,11 @@ void DiskCopy42Image::write(unsigned block, const void *bp)
}
BlockCache *DiskCopy42Image::createBlockCache()
BlockCachePointer DiskCopy42Image::createBlockCache()
{
// if not readonly, mark changed so crc will be updated at close.
if (!readOnly()) _changed = true;
return new MappedBlockCache(this, address());
return MappedBlockCache::Create(shared_from_this(), address());
}

View File

@ -12,24 +12,29 @@ class DiskCopy42Image : public DiskImage {
public:
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 BlockCache *createBlockCache();
virtual BlockCachePointer createBlockCache();
DiskCopy42Image();
DiskCopy42Image(MappedFile *);
private:
DiskCopy42Image(const char *name, bool readOnly);
DiskCopy42Image(MappedFile *);
static void Validate(MappedFile *);
bool _changed;
};

View File

@ -14,16 +14,14 @@
#include <Cache/MappedBlockCache.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
using namespace Device;
using ProFUSE::Exception;
using ProFUSE::POSIXException;
/*
DiskImage::DiskImage(const char *name, bool readOnly)
{
File fd(name, readOnly ? O_RDONLY : O_RDWR);
@ -34,14 +32,14 @@ DiskImage::DiskImage(const char *name, bool readOnly)
_readOnly = readOnly;
_adaptor = NULL;
}
*/
DiskImage::DiskImage(MappedFile *file)
{
_file.adopt(*file);
_blocks = 0;
_readOnly = file->readOnly();
_readOnly = _file.readOnly();
_adaptor = NULL;
}
@ -74,7 +72,7 @@ void DiskImage::read(unsigned block, void *bp)
#define __METHOD__ "DiskImage::read"
if (block >= _blocks)
throw Exception(__METHOD__ ": Invalid block.");
throw ::Exception(__METHOD__ ": Invalid block.");
_adaptor->readBlock(block, bp);
}
@ -85,7 +83,7 @@ void DiskImage::write(unsigned block, const void *bp)
#define __METHOD__ "DiskImage::write"
if (block >= _blocks)
throw Exception(__METHOD__ ": Invalid block.");
throw ::Exception(__METHOD__ ": Invalid block.");
_adaptor->writeBlock(block, bp);
}
@ -97,18 +95,11 @@ void DiskImage::sync()
if (_file.isValid()) return _file.sync();
throw Exception(__METHOD__ ": File not set.");
throw ::Exception(__METHOD__ ": File not set.");
}
/*
ProDOSOrderDiskImage::ProDOSOrderDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
*/
ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
@ -119,35 +110,52 @@ ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) :
setAdaptor(new POAdaptor(address()));
}
ProDOSOrderDiskImage *ProDOSOrderDiskImage::Create(const char *name, size_t blocks)
BlockDevicePointer ProDOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = MappedFile::Create(name, blocks * 512);
return new ProDOSOrderDiskImage(file);
//return BlockDevicePointer(new ProDOSOrderDiskImage(file));
return MAKE_SHARED(ProDOSOrderDiskImage, file);
}
ProDOSOrderDiskImage *ProDOSOrderDiskImage::Open(MappedFile *file)
BlockDevicePointer ProDOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
return new ProDOSOrderDiskImage(file);
//return BlockDevicePointer(new ProDOSOrderDiskImage(file));
return MAKE_SHARED(ProDOSOrderDiskImage, file);
}
void ProDOSOrderDiskImage::Validate(MappedFile *f)
bool ProDOSOrderDiskImage::Validate(MappedFile *f, const std::nothrow_t &)
{
#undef __METHOD__
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
#undef __METHOD__
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
if (!f || !f->isValid()) throw Exception(__METHOD__ ": File not set.");
size_t size = f->length();
if (size % 512)
throw Exception(__METHOD__ ": Invalid file format.");
return false;
return true;
}
BlockCache *ProDOSOrderDiskImage::createBlockCache()
bool ProDOSOrderDiskImage::Validate(MappedFile *f)
{
return new MappedBlockCache(this, address());
#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 -
@ -162,6 +170,8 @@ DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) :
}
*/
DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) :
DiskImage(file)
{
@ -172,28 +182,45 @@ DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) :
}
DOSOrderDiskImage *DOSOrderDiskImage::Create(const char *name, size_t blocks)
BlockDevicePointer DOSOrderDiskImage::Create(const char *name, size_t blocks)
{
MappedFile *file = MappedFile::Create(name, blocks * 512);
return new DOSOrderDiskImage(file);
//return BlockDevicePointer(new DOSOrderDiskImage(file));
return MAKE_SHARED(DOSOrderDiskImage, file);
}
DOSOrderDiskImage *DOSOrderDiskImage::Open(MappedFile *file)
BlockDevicePointer DOSOrderDiskImage::Open(MappedFile *file)
{
Validate(file);
return new DOSOrderDiskImage(file);
//return BlockDevicePointer(new DOSOrderDiskImage(file));
return MAKE_SHARED(DOSOrderDiskImage, file);
}
void DOSOrderDiskImage::Validate(MappedFile *f)
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 (!f || !f->isValid()) throw ::Exception(__METHOD__ ": File not set.");
size_t size = f->length();
if (size % 512)
throw Exception(__METHOD__ ": Invalid file format.");
if (!Validate(f, std::nothrow))
throw ::Exception(__METHOD__ ": Invalid file format.");
return true;
}

View File

@ -4,8 +4,6 @@
#include <stdint.h>
#include <sys/types.h>
#include <ProFUSE/Exception.h>
#include <Device/BlockDevice.h>
#include <Device/Adaptor.h>
@ -30,10 +28,9 @@ public:
protected:
DiskImage();
DiskImage(MappedFile *file = 0);
DiskImage(const char *name, bool readOnly);
void setBlocks(unsigned blocks) { _blocks = blocks; }
@ -58,32 +55,37 @@ class ProDOSOrderDiskImage : public DiskImage {
public:
static ProDOSOrderDiskImage *Create(const char *name, size_t blocks);
static ProDOSOrderDiskImage *Open(MappedFile *);
static BlockDevicePointer Create(const char *name, size_t blocks);
static BlockDevicePointer Open(MappedFile *);
virtual BlockCache *createBlockCache();
private:
ProDOSOrderDiskImage(const char *name, bool readOnly);
virtual BlockCachePointer createBlockCache();
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
ProDOSOrderDiskImage(MappedFile *);
static void Validate(MappedFile *);
private:
ProDOSOrderDiskImage();
};
class DOSOrderDiskImage : public DiskImage {
public:
static DOSOrderDiskImage *Create(const char *name, size_t blocks);
static DOSOrderDiskImage *Open(MappedFile *);
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(const char *name, bool readOnly);
DOSOrderDiskImage();
DOSOrderDiskImage(MappedFile *);
static void Validate(MappedFile *);
};

View File

@ -25,14 +25,17 @@
#include <sys/disk.h>
#endif
#ifdef __minix
#include <minix/partition.h>
#endif
#include <Device/RawDevice.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)
@ -43,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.
@ -62,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;
@ -86,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; //
@ -105,25 +108,47 @@ void RawDevice::devSize(int fd)
#undef __METHOD__
#define __METHOD__ "RawDevice::devSize"
unisgned blockSize;
unsigned blockSize;
off_t mediaSize;
if (::ioctl(fd, DIOCGSECTORSIZE, &blockSize)
throw POSIXException(__METHOD__ ": Unable to determine block size.", errno);
throw POSIX::Exception(__METHOD__ ": Unable to determine block size.", errno);
if (::ioctl(fd, DIOCGMEDIASIZE, &mediaSize)
throw POSIXException(__METHOD__ ": Unable to determine media size.", errno);
throw POSIX::Exception(__METHOD__ ": Unable to determine media size.", errno);
_blockSize = blockSize;
_size = mediaSize;
_blocks = mediaSize / blockSize;
_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, bool readOnly) :
_file(name, readOnly)
RawDevice::RawDevice(const char *name, File::FileFlags flags) :
_file(name, flags)
{
#undef __METHOD__
#define __METHOD__ "RawDevice::RawDevice"
@ -131,10 +156,10 @@ RawDevice::RawDevice(const char *name, bool readOnly) :
if (!_file.isValid())
{
throw new Exception(__METHOD__ ": Invalid file handle.");
throw ::Exception(__METHOD__ ": Invalid file handle.");
}
_readOnly = readOnly;
_readOnly = flags == File::ReadOnly;
_size = 0;
_blocks = 0;
_blockSize = 0;
@ -143,7 +168,7 @@ RawDevice::RawDevice(const char *name, bool readOnly) :
devSize(_file.fd());
}
RawDevice::RawDevice(File& file, bool readOnly) :
RawDevice::RawDevice(File& file, File::FileFlags flags) :
_file(file)
{
#undef __METHOD__
@ -152,10 +177,10 @@ RawDevice::RawDevice(File& file, bool readOnly) :
if (!_file.isValid())
{
throw new Exception(__METHOD__ ": Invalid file handle.");
throw ::Exception(__METHOD__ ": Invalid file handle.");
}
_readOnly = readOnly;
_readOnly = flags == File::ReadOnly;
_size = 0;
_blocks = 0;
_blockSize = 0;
@ -170,9 +195,10 @@ RawDevice::~RawDevice()
}
RawDevice *RawDevice::Open(const char *name, bool readOnly)
BlockDevicePointer RawDevice::Open(const char *name, File::FileFlags flags)
{
return new RawDevice(name, readOnly);
//return BlockDevicePointer(new RawDevice(name, flags));
return MAKE_SHARED(RawDevice, name, flags);
}
@ -181,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(_file.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.");
}
@ -204,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(_file.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.");
}
@ -226,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(_file.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.");
}
@ -248,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(_file.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.");
}
@ -289,6 +315,6 @@ void RawDevice::sync()
if (_readOnly) return;
if (::fsync(_file.fd()) < 0)
throw POSIXException(__METHOD__ ": fsync error.", errno);
throw POSIX::Exception(__METHOD__ ": fsync error.", errno);
}

View File

@ -15,11 +15,9 @@ namespace Device {
class RawDevice : public BlockDevice {
public:
RawDevice(const char *name, bool readOnly);
RawDevice(File& file, bool readOnly);
static RawDevice *Open(const char *name, bool readOnly);
static BlockDevicePointer Open(const char *name, File::FileFlags flags);
virtual ~RawDevice();
@ -35,18 +33,21 @@ public:
virtual void sync();
virtual unsigned blocks();
private:
RawDevice(const char *name, File::FileFlags flags);
RawDevice(File& file, File::FileFlags flags);
private:
void devSize(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

@ -3,7 +3,7 @@
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
#include <Cache/MappedBlockCache.h>
#include <Cache/ConcreteBlockCache.h>
@ -11,23 +11,8 @@
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)
{
@ -52,10 +37,10 @@ UniversalDiskImage::UniversalDiskImage(MappedFile *file) :
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];
@ -92,13 +77,17 @@ UniversalDiskImage *UniversalDiskImage::Create(const char *name, size_t blocks)
std::memcpy(file->address(), header.buffer(), 64);
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);
}
@ -107,42 +96,51 @@ 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();
bool ok = false;
unsigned blocks = 0;
unsigned offset = 0;
do {
if (size < 64) break;
if (std::memcmp(data, "2IMG", 4)) break;
// only prodos supported, for now...
// TODO -- Dos Order, Nibble support.
if (Read32(data, 0x0c) != 1) break;
blocks = Read32(data, 0x14);
offset = Read32(data, 0x18);
// file size == blocks * 512
if (Read32(data, 0x1c) != blocks * 512) break;
if (offset + blocks * 512 > size) break;
ok = true;
} while (false);
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
unsigned fileSize = 0;
if (size < 64) return false;
if (std::memcmp(data, "2IMG", 4)) return false;
// 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;
}
@ -152,11 +150,11 @@ bool UniversalDiskImage::readOnly()
}
BlockCache *UniversalDiskImage::createBlockCache()
BlockCachePointer UniversalDiskImage::createBlockCache()
{
if (_format == 1)
{
return new MappedBlockCache(this, _dataOffset + (uint8_t *)address());
return MappedBlockCache::Create(shared_from_this(), _dataOffset + (uint8_t *)address());
}
return DiskImage::createBlockCache();

View File

@ -14,20 +14,22 @@ public:
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();
virtual BlockCache *createBlockCache();
virtual BlockCachePointer createBlockCache();
static bool Validate(MappedFile *, const std::nothrow_t &);
static bool Validate(MappedFile *);
UniversalDiskImage(MappedFile *);
private:
UniversalDiskImage(const char *name, bool readOnly);
UniversalDiskImage(MappedFile *);
static void Validate(MappedFile *);
UniversalDiskImage();
uint32_t _format;
uint32_t _flags;

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()
@ -31,9 +27,15 @@ File::File(const char *name, int flags, const std::nothrow_t&)
_fd = ::open(name, flags);
}
File::File(const char *name, bool readOnly, const std::nothrow_t&)
File::File(const char *name, int flags, mode_t mode, const std::nothrow_t&)
{
_fd = ::open(name, readOnly ? O_RDONLY : O_RDWR);
_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)
@ -43,18 +45,28 @@ 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, bool readOnly)
File::File(const char *name, int flags, mode_t mode)
{
#undef __METHOD__
#define __METHOD__ "File::File"
_fd = ::open(name, readOnly ? O_RDONLY : O_RDWR);
_fd = ::open(name, flags, mode);
if (_fd < 0)
throw POSIXException( __METHOD__ ": open", errno);
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);
}
@ -63,6 +75,13 @@ File::~File()
close();
}
int File::release()
{
int tmp = _fd;
_fd = -1;
return tmp;
}
void File::close()
{
#undef __METHOD__
@ -70,11 +89,14 @@ 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);
*/
}
}

View File

@ -7,19 +7,28 @@
#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, bool readOnly);
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, bool readOnly, 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();
@ -30,6 +39,8 @@ class File {
int fd() const { return _fd; }
int release();
void close();
void adopt(File &f);

View File

@ -4,10 +4,8 @@
#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()
{
@ -27,29 +25,41 @@ MappedFile::MappedFile(MappedFile &mf)
mf._readOnly = true;
}
MappedFile::MappedFile(const File &f, bool readOnly, size_t size)
MappedFile::MappedFile(const File &f, File::FileFlags flags, size_t size)
{
_length = -1;
_address = MAP_FAILED;
_readOnly = readOnly;
_readOnly = true;
init(f, readOnly, size);
init(f, flags == File::ReadOnly, size);
}
MappedFile::MappedFile(const char *name, bool readOnly)
MappedFile::MappedFile(const char *name, File::FileFlags flags)
{
File f(name, readOnly);
File f(name, flags);
_length = -1;
_address = MAP_FAILED;
_readOnly = readOnly;
_readOnly = true;
init(f, readOnly, 0);
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()
{
@ -69,16 +79,16 @@ void MappedFile::init(const File &f, bool readOnly, size_t size)
// close enough
if (f.fd() < 0)
throw POSIXException( __METHOD__, EBADF);
throw POSIX::Exception( __METHOD__, EBADF);
if (!size)
{
if (::fstat(f.fd(), &st) != 0)
throw POSIXException(__METHOD__ ": fstat", errno);
throw POSIX::Exception(__METHOD__ ": fstat", errno);
if (!S_ISREG(st.st_mode))
throw POSIXException(__METHOD__, ENODEV);
throw POSIX::Exception(__METHOD__, ENODEV);
size = st.st_size;
}
@ -87,7 +97,7 @@ void MappedFile::init(const File &f, bool readOnly, size_t size)
_address = ::mmap(0, _length, prot, flags, f.fd(), 0);
if (_address == MAP_FAILED)
throw POSIXException(__METHOD__ ": mmap", errno);
throw POSIX::Exception(__METHOD__ ": mmap", errno);
_readOnly = readOnly;
}
@ -101,15 +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);
*/
}
}
@ -121,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);
}
}
@ -154,15 +171,15 @@ MappedFile *MappedFile::Create(const char *name, size_t size)
if (!fd.isValid())
{
throw POSIXException(__METHOD__ ": Unable to create file.", errno);
throw POSIX::Exception(__METHOD__ ": Unable to create file.", errno);
}
// TODO -- is ftruncate portable?
if (::ftruncate(fd.fd(), size) < 0)
{
// TODO -- unlink?
throw POSIXException(__METHOD__ ": Unable to truncate file.", errno);
throw POSIX::Exception(__METHOD__ ": Unable to truncate file.", errno);
}
return new MappedFile(fd, false, size);
return new MappedFile(fd, File::ReadWrite, size);
}

View File

@ -1,6 +1,8 @@
#ifndef __MAPPED_FILE_H__
#define __MAPPED_FILE_H__
#include <new>
#include <sys/mman.h>
#include <File/File.h>
@ -12,8 +14,9 @@ class MappedFile {
MappedFile();
MappedFile(MappedFile&);
MappedFile(const File &f, bool readOnly, size_t size = -1);
MappedFile(const char *name, bool readOnly);
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();

280
Makefile
View File

@ -1,99 +1,218 @@
CC = g++
CPPFLAGS += -Wall -W -I. -O2 -g
LDFLAGS += -lpthread
fuse_pascal_LDFLAGS += -lfuse
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)
xattr: xattr.o
$(CC) $^ -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
newfs_pascal: newfs_pascal.o \
Cache/BlockCache.o Cache/ConcreteBlockCache.o Cache/MappedBlockCache.o \
Device/Adaptor.o Device/BlockDevice.o Device/DavexDiskImage.o \
Device/DiskCopy42Image.o Device/DiskImage.o Device/RawDevice.o \
Device/UniversalDiskImage.o \
Endian/Endian.o \
File/File.o File/MappedFile.o \
ProFUSE/Exception.o ProFUSE/Lock.o \
Pascal/Date.o Pascal/Entry.o Pascal/FileEntry.o Pascal/VolumeEntry.o
apfm: apfm.o \
Cache/BlockCache.o Cache/ConcreteBlockCache.o Cache/MappedBlockCache.o \
Device/Adaptor.o Device/BlockDevice.o Device/DavexDiskImage.o \
Device/DiskCopy42Image.o Device/DiskImage.o Device/RawDevice.o \
Device/UniversalDiskImage.o \
Endian/Endian.o \
File/File.o File/MappedFile.o \
ProFUSE/Exception.o ProFUSE/Lock.o \
Pascal/Date.o Pascal/Entry.o Pascal/FileEntry.o Pascal/VolumeEntry.o
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
fuse_pascal: fuse_pascal.o fuse_pascal_ops.o \
Cache/BlockCache.o Cache/ConcreteBlockCache.o Cache/MappedBlockCache.o \
Device/Adaptor.o Device/BlockDevice.o Device/DavexDiskImage.o \
Device/DiskCopy42Image.o Device/DiskImage.o Device/RawDevice.o \
Device/UniversalDiskImage.o \
Endian/Endian.o \
File/File.o File/MappedFile.o \
ProFUSE/Exception.o ProFUSE/Lock.o \
Pascal/Date.o Pascal/Entry.o Pascal/FileEntry.o Pascal/VolumeEntry.o
$(CC) -lfuse $(LDFLAGS) $^ -o $@
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}
xattr.o: xattr.cpp
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: newfs_pascal.cpp Device/BlockDevice.h ProFUSE/Exception.h \
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/File.h Pascal/Date.h
Pascal/Pascal.h Pascal/Date.h
fuse_pascal.o: fuse_pascal.cpp Pascal/File.h Pascal/Date.h \
ProFUSE/Exception.h Device/BlockDevice.h Device/TrackSector.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: fuse_pascal_ops.cpp Pascal/File.h Pascal/Date.h \
ProFUSE/auto.h ProFUSE/Exception.h
fuse_pascal_ops.o: bin/fuse_pascal_ops.cpp Pascal/Pascal.h Pascal/Date.h \
Common/auto.h Common/Exception.h
apfm.o: apfm.cpp Pascal/File.h Pascal/Date.h Device/BlockDevice.h \
ProFUSE/Exception.h Device/TrackSector.h Cache/BlockCache.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 ProFUSE/Exception.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 \
ProFUSE/Exception.h
Common/Exception.h
Device/Adaptor.o: Device/Adaptor.cpp Device/Adaptor.h Device/TrackSector.h \
ProFUSE/Exception.h
Common/Exception.h
Device/BlockDevice.o: Device/BlockDevice.cpp Device/BlockDevice.h \
ProFUSE/Exception.h Device/TrackSector.h Cache/BlockCache.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 ProFUSE/Exception.h Device/TrackSector.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 ProFUSE/Exception.h Device/TrackSector.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 \
ProFUSE/Exception.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 \
ProFUSE/Exception.h Device/TrackSector.h Cache/BlockCache.h File/File.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 ProFUSE/Exception.h \
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 \
@ -102,37 +221,60 @@ Device/UniversalDiskImage.o: Device/UniversalDiskImage.cpp \
Endian/Endian.o: Endian/Endian.cpp Endian/Endian.h
Cache/BlockCache.o: Cache/BlockCache.cpp Cache/BlockCache.h \
Device/BlockDevice.h ProFUSE/Exception.h Device/TrackSector.h \
ProFUSE/auto.h
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
Common/auto.h
Cache/ConcreteBlockCache.o: Cache/ConcreteBlockCache.cpp \
Device/BlockDevice.h \
ProFUSE/Exception.h Device/TrackSector.h Cache/BlockCache.h \
Cache/ConcreteBlockCache.h ProFUSE/auto.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 ProFUSE/Exception.h \
Cache/BlockCache.h Device/BlockDevice.h Common/Exception.h \
Device/TrackSector.h
ProFUSE/Exception.o: ProFUSE/Exception.cpp ProFUSE/Exception.h
Common/Exception.o: Common/Exception.cpp Common/Exception.h
Common/Lock.o: Common/Lock.cpp Common/Lock.h
ProFUSE/Lock.o: ProFUSE/Lock.cpp ProFUSE/Lock.h
Pascal/Date.o: Pascal/Date.cpp Pascal/Date.h
Pascal/Entry.o: Pascal/Entry.cpp Pascal/File.h Pascal/Date.h \
ProFUSE/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/File.h Pascal/Date.h \
ProFUSE/auto.h ProFUSE/Exception.h Endian/Endian.h Endian/IOBuffer.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/VolumeEntry.o: Pascal/VolumeEntry.cpp Pascal/File.h Pascal/Date.h \
ProFUSE/auto.h ProFUSE/Exception.h Endian/Endian.h Endian/IOBuffer.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

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,868 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objects = {
/* Begin PBXBuildFile section */
B63EFCD211A36C8B00C90DCE /* Endian.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA7511A0948500C90DCE /* Endian.cpp */; };
B63EFCD311A36C8B00C90DCE /* BlockDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9511A094E000C90DCE /* BlockDevice.cpp */; };
B63EFCD411A36C8B00C90DCE /* DavexDiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9711A094E000C90DCE /* DavexDiskImage.cpp */; };
B63EFCD511A36C8B00C90DCE /* DiskCopy42Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9B11A094E000C90DCE /* DiskCopy42Image.cpp */; };
B63EFCD611A36C8B00C90DCE /* DiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9D11A094E000C90DCE /* DiskImage.cpp */; };
B63EFCD711A36C8B00C90DCE /* RawDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAA111A094E000C90DCE /* RawDevice.cpp */; };
B63EFCD811A36C8B00C90DCE /* UniversalDiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAA411A094E000C90DCE /* UniversalDiskImage.cpp */; };
B63EFCD911A36C8B00C90DCE /* BlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAB611A098C400C90DCE /* BlockCache.cpp */; };
B63EFCDA11A36C8B00C90DCE /* ConcreteBlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAB811A098C400C90DCE /* ConcreteBlockCache.cpp */; };
B63EFCDB11A36C8B00C90DCE /* MappedBlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFABA11A098C400C90DCE /* MappedBlockCache.cpp */; };
B63EFCDC11A36C8B00C90DCE /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAC911A09DAA00C90DCE /* File.cpp */; };
B63EFCDD11A36C8B00C90DCE /* MappedFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFACB11A09DAA00C90DCE /* MappedFile.cpp */; };
B63EFCDE11A36C8B00C90DCE /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAD111A09DB500C90DCE /* Exception.cpp */; };
B63EFCDF11A36C8B00C90DCE /* Lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAD311A09DB500C90DCE /* Lock.cpp */; };
B63EFCE011A36C8B00C90DCE /* Adaptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFB0711A0AC4800C90DCE /* Adaptor.cpp */; };
B63EFCE111A36C8B00C90DCE /* Date.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBE811A244EE00C90DCE /* Date.cpp */; };
B63EFCE211A36C8B00C90DCE /* Entry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBEA11A244EE00C90DCE /* Entry.cpp */; };
B63EFCE311A36C8B00C90DCE /* FileEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBEC11A244EE00C90DCE /* FileEntry.cpp */; };
B63EFCE511A36C8B00C90DCE /* VolumeEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBF111A244EE00C90DCE /* VolumeEntry.cpp */; };
B63EFCE911A36C8B00C90DCE /* apfm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFC9F11A35F8F00C90DCE /* apfm.cpp */; };
B63EFE8511A4888500C90DCE /* Endian.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA7511A0948500C90DCE /* Endian.cpp */; };
B63EFE8611A4888500C90DCE /* BlockDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9511A094E000C90DCE /* BlockDevice.cpp */; };
B63EFE8711A4888500C90DCE /* DavexDiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9711A094E000C90DCE /* DavexDiskImage.cpp */; };
B63EFE8811A4888500C90DCE /* DiskCopy42Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9B11A094E000C90DCE /* DiskCopy42Image.cpp */; };
B63EFE8911A4888500C90DCE /* DiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9D11A094E000C90DCE /* DiskImage.cpp */; };
B63EFE8A11A4888500C90DCE /* RawDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAA111A094E000C90DCE /* RawDevice.cpp */; };
B63EFE8B11A4888500C90DCE /* UniversalDiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAA411A094E000C90DCE /* UniversalDiskImage.cpp */; };
B63EFE8C11A4888500C90DCE /* BlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAB611A098C400C90DCE /* BlockCache.cpp */; };
B63EFE8D11A4888500C90DCE /* ConcreteBlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAB811A098C400C90DCE /* ConcreteBlockCache.cpp */; };
B63EFE8E11A4888500C90DCE /* MappedBlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFABA11A098C400C90DCE /* MappedBlockCache.cpp */; };
B63EFE8F11A4888500C90DCE /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAC911A09DAA00C90DCE /* File.cpp */; };
B63EFE9011A4888500C90DCE /* MappedFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFACB11A09DAA00C90DCE /* MappedFile.cpp */; };
B63EFE9111A4888500C90DCE /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAD111A09DB500C90DCE /* Exception.cpp */; };
B63EFE9211A4888500C90DCE /* Lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAD311A09DB500C90DCE /* Lock.cpp */; };
B63EFE9311A4888500C90DCE /* Adaptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFB0711A0AC4800C90DCE /* Adaptor.cpp */; };
B63EFE9411A4888500C90DCE /* Date.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBE811A244EE00C90DCE /* Date.cpp */; };
B63EFE9511A4888500C90DCE /* Entry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBEA11A244EE00C90DCE /* Entry.cpp */; };
B63EFE9611A4888500C90DCE /* FileEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBEC11A244EE00C90DCE /* FileEntry.cpp */; };
B63EFE9711A4888500C90DCE /* VolumeEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBF111A244EE00C90DCE /* VolumeEntry.cpp */; };
B63EFE9911A4888500C90DCE /* newfs_pascal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFC9B11A35F7B00C90DCE /* newfs_pascal.cpp */; };
B656AE0811A84F6C00AB578A /* libfuse_ino64.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B656ADEB11A84D2800AB578A /* libfuse_ino64.dylib */; };
B656AE0911A84F7700AB578A /* Endian.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA7511A0948500C90DCE /* Endian.cpp */; };
B656AE0A11A84F7700AB578A /* BlockDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9511A094E000C90DCE /* BlockDevice.cpp */; };
B656AE0B11A84F7700AB578A /* DavexDiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9711A094E000C90DCE /* DavexDiskImage.cpp */; };
B656AE0C11A84F7700AB578A /* DiskCopy42Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9B11A094E000C90DCE /* DiskCopy42Image.cpp */; };
B656AE0D11A84F7700AB578A /* DiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9D11A094E000C90DCE /* DiskImage.cpp */; };
B656AE0E11A84F7700AB578A /* RawDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAA111A094E000C90DCE /* RawDevice.cpp */; };
B656AE0F11A84F7700AB578A /* UniversalDiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAA411A094E000C90DCE /* UniversalDiskImage.cpp */; };
B656AE1011A84F7700AB578A /* BlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAB611A098C400C90DCE /* BlockCache.cpp */; };
B656AE1111A84F7700AB578A /* ConcreteBlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAB811A098C400C90DCE /* ConcreteBlockCache.cpp */; };
B656AE1211A84F7700AB578A /* MappedBlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFABA11A098C400C90DCE /* MappedBlockCache.cpp */; };
B656AE1311A84F7700AB578A /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAC911A09DAA00C90DCE /* File.cpp */; };
B656AE1411A84F7700AB578A /* MappedFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFACB11A09DAA00C90DCE /* MappedFile.cpp */; };
B656AE1511A84F7700AB578A /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAD111A09DB500C90DCE /* Exception.cpp */; };
B656AE1611A84F7700AB578A /* Lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAD311A09DB500C90DCE /* Lock.cpp */; };
B656AE1711A84F7700AB578A /* Adaptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFB0711A0AC4800C90DCE /* Adaptor.cpp */; };
B656AE1811A84F7700AB578A /* Date.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBE811A244EE00C90DCE /* Date.cpp */; };
B656AE1911A84F7700AB578A /* Entry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBEA11A244EE00C90DCE /* Entry.cpp */; };
B656AE1A11A84F7700AB578A /* FileEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBEC11A244EE00C90DCE /* FileEntry.cpp */; };
B656AE1B11A84F7700AB578A /* VolumeEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBF111A244EE00C90DCE /* VolumeEntry.cpp */; };
B656AE1D11A84F9300AB578A /* fuse_pascal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFC9911A35F7200C90DCE /* fuse_pascal.cpp */; };
B656AE1E11A84F9300AB578A /* fuse_pascal_ops.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFC9D11A35F8200C90DCE /* fuse_pascal_ops.cpp */; };
B656AF0811AA106400AB578A /* NibbleTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B656AEFE11AA0F8C00AB578A /* NibbleTest.cpp */; };
B656AF0911AA106C00AB578A /* Endian.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA7511A0948500C90DCE /* Endian.cpp */; };
B656AF0A11AA106C00AB578A /* BlockDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9511A094E000C90DCE /* BlockDevice.cpp */; };
B656AF0B11AA106C00AB578A /* DavexDiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9711A094E000C90DCE /* DavexDiskImage.cpp */; };
B656AF0C11AA106C00AB578A /* DiskCopy42Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9B11A094E000C90DCE /* DiskCopy42Image.cpp */; };
B656AF0D11AA106C00AB578A /* DiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFA9D11A094E000C90DCE /* DiskImage.cpp */; };
B656AF0E11AA106C00AB578A /* RawDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAA111A094E000C90DCE /* RawDevice.cpp */; };
B656AF0F11AA106C00AB578A /* UniversalDiskImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAA411A094E000C90DCE /* UniversalDiskImage.cpp */; };
B656AF1011AA106C00AB578A /* BlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAB611A098C400C90DCE /* BlockCache.cpp */; };
B656AF1111AA106C00AB578A /* ConcreteBlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAB811A098C400C90DCE /* ConcreteBlockCache.cpp */; };
B656AF1211AA106C00AB578A /* MappedBlockCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFABA11A098C400C90DCE /* MappedBlockCache.cpp */; };
B656AF1311AA106C00AB578A /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAC911A09DAA00C90DCE /* File.cpp */; };
B656AF1411AA106C00AB578A /* MappedFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFACB11A09DAA00C90DCE /* MappedFile.cpp */; };
B656AF1511AA106C00AB578A /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAD111A09DB500C90DCE /* Exception.cpp */; };
B656AF1611AA106C00AB578A /* Lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFAD311A09DB500C90DCE /* Lock.cpp */; };
B656AF1711AA106C00AB578A /* Adaptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFB0711A0AC4800C90DCE /* Adaptor.cpp */; };
B656AF1811AA106C00AB578A /* Date.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBE811A244EE00C90DCE /* Date.cpp */; };
B656AF1911AA106C00AB578A /* Entry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBEA11A244EE00C90DCE /* Entry.cpp */; };
B656AF1A11AA106C00AB578A /* FileEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBEC11A244EE00C90DCE /* FileEntry.cpp */; };
B656AF1B11AA106C00AB578A /* VolumeEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B63EFBF111A244EE00C90DCE /* VolumeEntry.cpp */; };
B6F3648511AB363B0045E114 /* xattr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6F3647B11AB361D0045E114 /* xattr.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B63EFA7511A0948500C90DCE /* Endian.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Endian.cpp; sourceTree = "<group>"; };
B63EFA7611A0948500C90DCE /* Endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Endian.h; sourceTree = "<group>"; };
B63EFA7711A0948500C90DCE /* IOBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOBuffer.h; sourceTree = "<group>"; };
B63EFA9511A094E000C90DCE /* BlockDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BlockDevice.cpp; sourceTree = "<group>"; };
B63EFA9611A094E000C90DCE /* BlockDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockDevice.h; sourceTree = "<group>"; };
B63EFA9711A094E000C90DCE /* DavexDiskImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DavexDiskImage.cpp; sourceTree = "<group>"; };
B63EFA9811A094E000C90DCE /* DavexDiskImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DavexDiskImage.h; sourceTree = "<group>"; };
B63EFA9B11A094E000C90DCE /* DiskCopy42Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiskCopy42Image.cpp; sourceTree = "<group>"; };
B63EFA9C11A094E000C90DCE /* DiskCopy42Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiskCopy42Image.h; sourceTree = "<group>"; };
B63EFA9D11A094E000C90DCE /* DiskImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiskImage.cpp; sourceTree = "<group>"; };
B63EFA9E11A094E000C90DCE /* DiskImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiskImage.h; sourceTree = "<group>"; };
B63EFAA111A094E000C90DCE /* RawDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RawDevice.cpp; sourceTree = "<group>"; };
B63EFAA211A094E000C90DCE /* RawDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RawDevice.h; sourceTree = "<group>"; };
B63EFAA311A094E000C90DCE /* TrackSector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackSector.h; sourceTree = "<group>"; };
B63EFAA411A094E000C90DCE /* UniversalDiskImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UniversalDiskImage.cpp; sourceTree = "<group>"; };
B63EFAA511A094E000C90DCE /* UniversalDiskImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UniversalDiskImage.h; sourceTree = "<group>"; };
B63EFAB611A098C400C90DCE /* BlockCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BlockCache.cpp; sourceTree = "<group>"; };
B63EFAB711A098C400C90DCE /* BlockCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockCache.h; sourceTree = "<group>"; };
B63EFAB811A098C400C90DCE /* ConcreteBlockCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConcreteBlockCache.cpp; sourceTree = "<group>"; };
B63EFAB911A098C400C90DCE /* ConcreteBlockCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConcreteBlockCache.h; sourceTree = "<group>"; };
B63EFABA11A098C400C90DCE /* MappedBlockCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MappedBlockCache.cpp; sourceTree = "<group>"; };
B63EFABB11A098C400C90DCE /* MappedBlockCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MappedBlockCache.h; sourceTree = "<group>"; };
B63EFAC911A09DAA00C90DCE /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = File.cpp; sourceTree = "<group>"; };
B63EFACA11A09DAA00C90DCE /* File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = File.h; sourceTree = "<group>"; };
B63EFACB11A09DAA00C90DCE /* MappedFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MappedFile.cpp; sourceTree = "<group>"; };
B63EFACC11A09DAA00C90DCE /* MappedFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MappedFile.h; sourceTree = "<group>"; };
B63EFAD011A09DB500C90DCE /* auto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = auto.h; sourceTree = "<group>"; };
B63EFAD111A09DB500C90DCE /* Exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Exception.cpp; sourceTree = "<group>"; };
B63EFAD211A09DB500C90DCE /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; };
B63EFAD311A09DB500C90DCE /* Lock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lock.cpp; sourceTree = "<group>"; };
B63EFAD411A09DB500C90DCE /* Lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Lock.h; sourceTree = "<group>"; };
B63EFB0611A0AC4800C90DCE /* Adaptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Adaptor.h; sourceTree = "<group>"; };
B63EFB0711A0AC4800C90DCE /* Adaptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Adaptor.cpp; sourceTree = "<group>"; };
B63EFBE811A244EE00C90DCE /* Date.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Date.cpp; sourceTree = "<group>"; };
B63EFBE911A244EE00C90DCE /* Date.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Date.h; sourceTree = "<group>"; };
B63EFBEA11A244EE00C90DCE /* Entry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Entry.cpp; sourceTree = "<group>"; };
B63EFBEB11A244EE00C90DCE /* File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = File.h; sourceTree = "<group>"; };
B63EFBEC11A244EE00C90DCE /* FileEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileEntry.cpp; sourceTree = "<group>"; };
B63EFBF111A244EE00C90DCE /* VolumeEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VolumeEntry.cpp; sourceTree = "<group>"; };
B63EFC4311A31B7200C90DCE /* IOBuffer.cpp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOBuffer.cpp.h; sourceTree = "<group>"; };
B63EFC9911A35F7200C90DCE /* fuse_pascal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fuse_pascal.cpp; sourceTree = "<group>"; };
B63EFC9B11A35F7B00C90DCE /* newfs_pascal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = newfs_pascal.cpp; sourceTree = "<group>"; };
B63EFC9D11A35F8200C90DCE /* fuse_pascal_ops.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fuse_pascal_ops.cpp; sourceTree = "<group>"; };
B63EFC9F11A35F8F00C90DCE /* apfm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = apfm.cpp; sourceTree = "<group>"; };
B63EFCCE11A36C7300C90DCE /* apfpm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = apfpm; sourceTree = BUILT_PRODUCTS_DIR; };
B63EFE8111A4886C00C90DCE /* newfs_pascal */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = newfs_pascal; sourceTree = BUILT_PRODUCTS_DIR; };
B656ADEB11A84D2800AB578A /* libfuse_ino64.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libfuse_ino64.dylib; path = /usr/local/lib/libfuse_ino64.dylib; sourceTree = "<absolute>"; };
B656AE0311A84F5800AB578A /* fuse_pascal */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fuse_pascal; sourceTree = BUILT_PRODUCTS_DIR; };
B656AEFE11AA0F8C00AB578A /* NibbleTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NibbleTest.cpp; sourceTree = "<group>"; };
B656AF0311AA105500AB578A /* NibbleTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = NibbleTest; sourceTree = BUILT_PRODUCTS_DIR; };
B6F3647B11AB361D0045E114 /* xattr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xattr.cpp; sourceTree = "<group>"; };
B6F3648011AB36260045E114 /* xattr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; name = xattr; path = build/Debug/xattr; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B63EFCCC11A36C7300C90DCE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
B63EFE7F11A4886C00C90DCE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
B656AE0111A84F5800AB578A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B656AE0811A84F6C00AB578A /* libfuse_ino64.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B656AF0111AA105500AB578A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
B6F3647E11AB36260045E114 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
08FB7794FE84155DC02AAC07 /* PROFuseX */ = {
isa = PBXGroup;
children = (
B6F3647B11AB361D0045E114 /* xattr.cpp */,
B63EFC9F11A35F8F00C90DCE /* apfm.cpp */,
B656AEFE11AA0F8C00AB578A /* NibbleTest.cpp */,
B63EFC9B11A35F7B00C90DCE /* newfs_pascal.cpp */,
B63EFC9911A35F7200C90DCE /* fuse_pascal.cpp */,
B63EFC9D11A35F8200C90DCE /* fuse_pascal_ops.cpp */,
B63EFBE711A244EE00C90DCE /* Pascal */,
B63EFACF11A09DB500C90DCE /* ProFUSE */,
B63EFAC811A09DAA00C90DCE /* File */,
B63EFAB511A098C400C90DCE /* Cache */,
B63EFA9411A094E000C90DCE /* Device */,
B63EFA7411A0948500C90DCE /* Endian */,
1AB674ADFE9D54B511CA2CBB /* Products */,
B656ADED11A84D3200AB578A /* Libraries */,
);
name = PROFuseX;
sourceTree = "<group>";
};
1AB674ADFE9D54B511CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
B63EFCCE11A36C7300C90DCE /* apfpm */,
B63EFE8111A4886C00C90DCE /* newfs_pascal */,
B656AE0311A84F5800AB578A /* fuse_pascal */,
B656AF0311AA105500AB578A /* NibbleTest */,
B6F3648011AB36260045E114 /* xattr */,
);
name = Products;
sourceTree = "<group>";
};
B63EFA7411A0948500C90DCE /* Endian */ = {
isa = PBXGroup;
children = (
B63EFA7511A0948500C90DCE /* Endian.cpp */,
B63EFA7611A0948500C90DCE /* Endian.h */,
B63EFA7711A0948500C90DCE /* IOBuffer.h */,
B63EFC4311A31B7200C90DCE /* IOBuffer.cpp.h */,
);
path = Endian;
sourceTree = "<group>";
};
B63EFA9411A094E000C90DCE /* Device */ = {
isa = PBXGroup;
children = (
B63EFA9511A094E000C90DCE /* BlockDevice.cpp */,
B63EFA9611A094E000C90DCE /* BlockDevice.h */,
B63EFA9711A094E000C90DCE /* DavexDiskImage.cpp */,
B63EFA9811A094E000C90DCE /* DavexDiskImage.h */,
B63EFA9B11A094E000C90DCE /* DiskCopy42Image.cpp */,
B63EFA9C11A094E000C90DCE /* DiskCopy42Image.h */,
B63EFA9D11A094E000C90DCE /* DiskImage.cpp */,
B63EFA9E11A094E000C90DCE /* DiskImage.h */,
B63EFAA111A094E000C90DCE /* RawDevice.cpp */,
B63EFAA211A094E000C90DCE /* RawDevice.h */,
B63EFAA311A094E000C90DCE /* TrackSector.h */,
B63EFAA411A094E000C90DCE /* UniversalDiskImage.cpp */,
B63EFAA511A094E000C90DCE /* UniversalDiskImage.h */,
B63EFB0611A0AC4800C90DCE /* Adaptor.h */,
B63EFB0711A0AC4800C90DCE /* Adaptor.cpp */,
);
path = Device;
sourceTree = "<group>";
};
B63EFAB511A098C400C90DCE /* Cache */ = {
isa = PBXGroup;
children = (
B63EFAB611A098C400C90DCE /* BlockCache.cpp */,
B63EFAB711A098C400C90DCE /* BlockCache.h */,
B63EFAB811A098C400C90DCE /* ConcreteBlockCache.cpp */,
B63EFAB911A098C400C90DCE /* ConcreteBlockCache.h */,
B63EFABA11A098C400C90DCE /* MappedBlockCache.cpp */,
B63EFABB11A098C400C90DCE /* MappedBlockCache.h */,
);
path = Cache;
sourceTree = "<group>";
};
B63EFAC811A09DAA00C90DCE /* File */ = {
isa = PBXGroup;
children = (
B63EFAC911A09DAA00C90DCE /* File.cpp */,
B63EFACA11A09DAA00C90DCE /* File.h */,
B63EFACB11A09DAA00C90DCE /* MappedFile.cpp */,
B63EFACC11A09DAA00C90DCE /* MappedFile.h */,
);
path = File;
sourceTree = "<group>";
};
B63EFACF11A09DB500C90DCE /* ProFUSE */ = {
isa = PBXGroup;
children = (
B63EFAD011A09DB500C90DCE /* auto.h */,
B63EFAD111A09DB500C90DCE /* Exception.cpp */,
B63EFAD211A09DB500C90DCE /* Exception.h */,
B63EFAD311A09DB500C90DCE /* Lock.cpp */,
B63EFAD411A09DB500C90DCE /* Lock.h */,
);
path = ProFUSE;
sourceTree = "<group>";
};
B63EFBE711A244EE00C90DCE /* Pascal */ = {
isa = PBXGroup;
children = (
B63EFBE811A244EE00C90DCE /* Date.cpp */,
B63EFBE911A244EE00C90DCE /* Date.h */,
B63EFBEA11A244EE00C90DCE /* Entry.cpp */,
B63EFBEB11A244EE00C90DCE /* File.h */,
B63EFBEC11A244EE00C90DCE /* FileEntry.cpp */,
B63EFBF111A244EE00C90DCE /* VolumeEntry.cpp */,
);
path = Pascal;
sourceTree = "<group>";
};
B656ADED11A84D3200AB578A /* Libraries */ = {
isa = PBXGroup;
children = (
B656ADEB11A84D2800AB578A /* libfuse_ino64.dylib */,
);
name = Libraries;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B63EFCCD11A36C7300C90DCE /* apfm */ = {
isa = PBXNativeTarget;
buildConfigurationList = B63EFCEC11A36CB800C90DCE /* Build configuration list for PBXNativeTarget "apfm" */;
buildPhases = (
B63EFCCB11A36C7300C90DCE /* Sources */,
B63EFCCC11A36C7300C90DCE /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = apfm;
productName = apfpm;
productReference = B63EFCCE11A36C7300C90DCE /* apfpm */;
productType = "com.apple.product-type.tool";
};
B63EFE8011A4886C00C90DCE /* newfs_pascal */ = {
isa = PBXNativeTarget;
buildConfigurationList = B63EFEB311A488C200C90DCE /* Build configuration list for PBXNativeTarget "newfs_pascal" */;
buildPhases = (
B63EFE7E11A4886C00C90DCE /* Sources */,
B63EFE7F11A4886C00C90DCE /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = newfs_pascal;
productName = newfs_pascal;
productReference = B63EFE8111A4886C00C90DCE /* newfs_pascal */;
productType = "com.apple.product-type.tool";
};
B656AE0211A84F5800AB578A /* fuse_pascal */ = {
isa = PBXNativeTarget;
buildConfigurationList = B656AE3411A84FA400AB578A /* Build configuration list for PBXNativeTarget "fuse_pascal" */;
buildPhases = (
B656AE0011A84F5800AB578A /* Sources */,
B656AE0111A84F5800AB578A /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = fuse_pascal;
productName = fuse_pascal;
productReference = B656AE0311A84F5800AB578A /* fuse_pascal */;
productType = "com.apple.product-type.tool";
};
B656AF0211AA105500AB578A /* NibbleTest */ = {
isa = PBXNativeTarget;
buildConfigurationList = B656AF2111AA10A100AB578A /* Build configuration list for PBXNativeTarget "NibbleTest" */;
buildPhases = (
B656AF0011AA105500AB578A /* Sources */,
B656AF0111AA105500AB578A /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = NibbleTest;
productName = NibbleTest;
productReference = B656AF0311AA105500AB578A /* NibbleTest */;
productType = "com.apple.product-type.tool";
};
B6F3647F11AB36260045E114 /* xattr */ = {
isa = PBXNativeTarget;
buildConfigurationList = B6F3648911AB36590045E114 /* Build configuration list for PBXNativeTarget "xattr" */;
buildPhases = (
B6F3647D11AB36260045E114 /* Sources */,
B6F3647E11AB36260045E114 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = xattr;
productName = xattr;
productReference = B6F3648011AB36260045E114 /* xattr */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "PROFuseX" */;
compatibilityVersion = "Xcode 3.1";
hasScannedForEncodings = 1;
mainGroup = 08FB7794FE84155DC02AAC07 /* PROFuseX */;
projectDirPath = "";
projectRoot = "";
targets = (
B63EFCCD11A36C7300C90DCE /* apfm */,
B63EFE8011A4886C00C90DCE /* newfs_pascal */,
B656AE0211A84F5800AB578A /* fuse_pascal */,
B656AF0211AA105500AB578A /* NibbleTest */,
B6F3647F11AB36260045E114 /* xattr */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
B63EFCCB11A36C7300C90DCE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B63EFCD211A36C8B00C90DCE /* Endian.cpp in Sources */,
B63EFCD311A36C8B00C90DCE /* BlockDevice.cpp in Sources */,
B63EFCD411A36C8B00C90DCE /* DavexDiskImage.cpp in Sources */,
B63EFCD511A36C8B00C90DCE /* DiskCopy42Image.cpp in Sources */,
B63EFCD611A36C8B00C90DCE /* DiskImage.cpp in Sources */,
B63EFCD711A36C8B00C90DCE /* RawDevice.cpp in Sources */,
B63EFCD811A36C8B00C90DCE /* UniversalDiskImage.cpp in Sources */,
B63EFCD911A36C8B00C90DCE /* BlockCache.cpp in Sources */,
B63EFCDA11A36C8B00C90DCE /* ConcreteBlockCache.cpp in Sources */,
B63EFCDB11A36C8B00C90DCE /* MappedBlockCache.cpp in Sources */,
B63EFCDC11A36C8B00C90DCE /* File.cpp in Sources */,
B63EFCDD11A36C8B00C90DCE /* MappedFile.cpp in Sources */,
B63EFCDE11A36C8B00C90DCE /* Exception.cpp in Sources */,
B63EFCDF11A36C8B00C90DCE /* Lock.cpp in Sources */,
B63EFCE011A36C8B00C90DCE /* Adaptor.cpp in Sources */,
B63EFCE111A36C8B00C90DCE /* Date.cpp in Sources */,
B63EFCE211A36C8B00C90DCE /* Entry.cpp in Sources */,
B63EFCE311A36C8B00C90DCE /* FileEntry.cpp in Sources */,
B63EFCE511A36C8B00C90DCE /* VolumeEntry.cpp in Sources */,
B63EFCE911A36C8B00C90DCE /* apfm.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B63EFE7E11A4886C00C90DCE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B63EFE8511A4888500C90DCE /* Endian.cpp in Sources */,
B63EFE8611A4888500C90DCE /* BlockDevice.cpp in Sources */,
B63EFE8711A4888500C90DCE /* DavexDiskImage.cpp in Sources */,
B63EFE8811A4888500C90DCE /* DiskCopy42Image.cpp in Sources */,
B63EFE8911A4888500C90DCE /* DiskImage.cpp in Sources */,
B63EFE8A11A4888500C90DCE /* RawDevice.cpp in Sources */,
B63EFE8B11A4888500C90DCE /* UniversalDiskImage.cpp in Sources */,
B63EFE8C11A4888500C90DCE /* BlockCache.cpp in Sources */,
B63EFE8D11A4888500C90DCE /* ConcreteBlockCache.cpp in Sources */,
B63EFE8E11A4888500C90DCE /* MappedBlockCache.cpp in Sources */,
B63EFE8F11A4888500C90DCE /* File.cpp in Sources */,
B63EFE9011A4888500C90DCE /* MappedFile.cpp in Sources */,
B63EFE9111A4888500C90DCE /* Exception.cpp in Sources */,
B63EFE9211A4888500C90DCE /* Lock.cpp in Sources */,
B63EFE9311A4888500C90DCE /* Adaptor.cpp in Sources */,
B63EFE9411A4888500C90DCE /* Date.cpp in Sources */,
B63EFE9511A4888500C90DCE /* Entry.cpp in Sources */,
B63EFE9611A4888500C90DCE /* FileEntry.cpp in Sources */,
B63EFE9711A4888500C90DCE /* VolumeEntry.cpp in Sources */,
B63EFE9911A4888500C90DCE /* newfs_pascal.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B656AE0011A84F5800AB578A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B656AE1D11A84F9300AB578A /* fuse_pascal.cpp in Sources */,
B656AE1E11A84F9300AB578A /* fuse_pascal_ops.cpp in Sources */,
B656AE0911A84F7700AB578A /* Endian.cpp in Sources */,
B656AE0A11A84F7700AB578A /* BlockDevice.cpp in Sources */,
B656AE0B11A84F7700AB578A /* DavexDiskImage.cpp in Sources */,
B656AE0C11A84F7700AB578A /* DiskCopy42Image.cpp in Sources */,
B656AE0D11A84F7700AB578A /* DiskImage.cpp in Sources */,
B656AE0E11A84F7700AB578A /* RawDevice.cpp in Sources */,
B656AE0F11A84F7700AB578A /* UniversalDiskImage.cpp in Sources */,
B656AE1011A84F7700AB578A /* BlockCache.cpp in Sources */,
B656AE1111A84F7700AB578A /* ConcreteBlockCache.cpp in Sources */,
B656AE1211A84F7700AB578A /* MappedBlockCache.cpp in Sources */,
B656AE1311A84F7700AB578A /* File.cpp in Sources */,
B656AE1411A84F7700AB578A /* MappedFile.cpp in Sources */,
B656AE1511A84F7700AB578A /* Exception.cpp in Sources */,
B656AE1611A84F7700AB578A /* Lock.cpp in Sources */,
B656AE1711A84F7700AB578A /* Adaptor.cpp in Sources */,
B656AE1811A84F7700AB578A /* Date.cpp in Sources */,
B656AE1911A84F7700AB578A /* Entry.cpp in Sources */,
B656AE1A11A84F7700AB578A /* FileEntry.cpp in Sources */,
B656AE1B11A84F7700AB578A /* VolumeEntry.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B656AF0011AA105500AB578A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B656AF0911AA106C00AB578A /* Endian.cpp in Sources */,
B656AF0A11AA106C00AB578A /* BlockDevice.cpp in Sources */,
B656AF0B11AA106C00AB578A /* DavexDiskImage.cpp in Sources */,
B656AF0C11AA106C00AB578A /* DiskCopy42Image.cpp in Sources */,
B656AF0D11AA106C00AB578A /* DiskImage.cpp in Sources */,
B656AF0E11AA106C00AB578A /* RawDevice.cpp in Sources */,
B656AF0F11AA106C00AB578A /* UniversalDiskImage.cpp in Sources */,
B656AF1011AA106C00AB578A /* BlockCache.cpp in Sources */,
B656AF1111AA106C00AB578A /* ConcreteBlockCache.cpp in Sources */,
B656AF1211AA106C00AB578A /* MappedBlockCache.cpp in Sources */,
B656AF1311AA106C00AB578A /* File.cpp in Sources */,
B656AF1411AA106C00AB578A /* MappedFile.cpp in Sources */,
B656AF1511AA106C00AB578A /* Exception.cpp in Sources */,
B656AF1611AA106C00AB578A /* Lock.cpp in Sources */,
B656AF1711AA106C00AB578A /* Adaptor.cpp in Sources */,
B656AF1811AA106C00AB578A /* Date.cpp in Sources */,
B656AF1911AA106C00AB578A /* Entry.cpp in Sources */,
B656AF1A11AA106C00AB578A /* FileEntry.cpp in Sources */,
B656AF1B11AA106C00AB578A /* VolumeEntry.cpp in Sources */,
B656AF0811AA106400AB578A /* NibbleTest.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B6F3647D11AB36260045E114 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B6F3648511AB363B0045E114 /* xattr.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1DEB923608733DC60010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
/usr/local/include/,
.,
);
ONLY_ACTIVE_ARCH = YES;
PREBINDING = NO;
SDKROOT = macosx10.6;
};
name = Debug;
};
1DEB923708733DC60010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PREBINDING = NO;
SDKROOT = macosx10.6;
};
name = Release;
};
B63EFCD011A36C7300C90DCE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = apfpm;
};
name = Debug;
};
B63EFCD111A36C7300C90DCE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = apfpm;
ZERO_LINK = NO;
};
name = Release;
};
B63EFE8311A4886C00C90DCE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = newfs_pascal;
};
name = Debug;
};
B63EFE8411A4886C00C90DCE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = newfs_pascal;
ZERO_LINK = NO;
};
name = Release;
};
B656AD7A11A84B5D00AB578A /* Debug fuse */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
/usr/local/include/,
.,
);
ONLY_ACTIVE_ARCH = YES;
PREBINDING = NO;
SDKROOT = macosx10.6;
};
name = "Debug fuse";
};
B656AD7C11A84B5D00AB578A /* Debug fuse */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = apfpm;
};
name = "Debug fuse";
};
B656AD7D11A84B5D00AB578A /* Debug fuse */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = newfs_pascal;
};
name = "Debug fuse";
};
B656AE0511A84F5900AB578A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = fuse_pascal;
};
name = Debug;
};
B656AE0611A84F5900AB578A /* Debug fuse */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = fuse_pascal;
};
name = "Debug fuse";
};
B656AE0711A84F5900AB578A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = fuse_pascal;
ZERO_LINK = NO;
};
name = Release;
};
B656AF0511AA105600AB578A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = NibbleTest;
};
name = Debug;
};
B656AF0611AA105600AB578A /* Debug fuse */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = NibbleTest;
};
name = "Debug fuse";
};
B656AF0711AA105600AB578A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = NibbleTest;
ZERO_LINK = NO;
};
name = Release;
};
B6F3648211AB36260045E114 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = xattr;
};
name = Debug;
};
B6F3648311AB36260045E114 /* Debug fuse */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = xattr;
};
name = "Debug fuse";
};
B6F3648411AB36260045E114 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = xattr;
ZERO_LINK = NO;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "PROFuseX" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB923608733DC60010E9CD /* Debug */,
B656AD7A11A84B5D00AB578A /* Debug fuse */,
1DEB923708733DC60010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B63EFCEC11A36CB800C90DCE /* Build configuration list for PBXNativeTarget "apfm" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B63EFCD011A36C7300C90DCE /* Debug */,
B656AD7C11A84B5D00AB578A /* Debug fuse */,
B63EFCD111A36C7300C90DCE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B63EFEB311A488C200C90DCE /* Build configuration list for PBXNativeTarget "newfs_pascal" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B63EFE8311A4886C00C90DCE /* Debug */,
B656AD7D11A84B5D00AB578A /* Debug fuse */,
B63EFE8411A4886C00C90DCE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B656AE3411A84FA400AB578A /* Build configuration list for PBXNativeTarget "fuse_pascal" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B656AE0511A84F5900AB578A /* Debug */,
B656AE0611A84F5900AB578A /* Debug fuse */,
B656AE0711A84F5900AB578A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B656AF2111AA10A100AB578A /* Build configuration list for PBXNativeTarget "NibbleTest" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B656AF0511AA105600AB578A /* Debug */,
B656AF0611AA105600AB578A /* Debug fuse */,
B656AF0711AA105600AB578A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B6F3648911AB36590045E114 /* Build configuration list for PBXNativeTarget "xattr" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B6F3648211AB36260045E114 /* Debug */,
B6F3648311AB36260045E114 /* Debug fuse */,
B6F3648411AB36260045E114 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
}

View File

@ -1,6 +1,6 @@
#include <Pascal/File.h>
#include <Pascal/Entry.h>
#include <ProFUSE/Exception.h>
#include <Common/Exception.h>
#include <Endian/Endian.h>
#include <Endian/IOBuffer.h>
@ -64,7 +64,6 @@ Entry::Entry()
_lastBlock = 0;
_fileKind = 0;
_inode = 0;
_parent = NULL;
_address = NULL;
}

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,221 +0,0 @@
#ifndef __PASCAL_FILE_H__
#define __PASCAL_FILE_H__
#include <Pascal/Date.h>
#include <vector>
#include <string>
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;
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; }
static bool Compress(std::string& text);
static bool Uncompress(std::string &text);
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;
unsigned _address;
};
class VolumeEntry : public Entry {
public:
static unsigned ValidName(const char *);
// 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;
FileEntry *fileByName(const char *name) 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 *);
void sync();
unsigned unlink(const char *name);
unsigned rename(const char *oldName, const char *newName);
unsigned krunch();
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
VolumeEntry();
void init(void *);
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);
unsigned _fileNameLength;
char _fileName[8];
unsigned _lastVolumeBlock;
unsigned _fileCount;
unsigned _accessTime;
Pascal::Date _lastBoot;
std::vector<FileEntry *> _files;
unsigned _inodeGenerator;
Device::BlockDevice *_device;
Device::BlockCache *_cache;
};
class FileEntry : public Entry {
public:
static unsigned ValidName(const char *);
static bool Compress(std::string& text);
static bool Uncompress(std::string& text);
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; }
protected:
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
private:
friend class VolumeEntry;
void setName(const char *name);
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,11 +3,14 @@
#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>
@ -24,14 +27,34 @@ enum {
kDLE = 16
};
#pragma mark -
#pragma mark FileEntry
/*
* _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)
{
@ -44,6 +67,7 @@ FileEntry::FileEntry(void *vp) :
_fileSize = 0;
_pageSize = NULL;
_maxFileSize = 0;
}
FileEntry::FileEntry(const char *name, unsigned fileKind)
@ -54,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;
@ -69,6 +93,7 @@ FileEntry::FileEntry(const char *name, unsigned fileKind)
_fileSize = 0;
_pageSize = NULL;
_maxFileSize = 0;
}
FileEntry::~FileEntry()
@ -77,6 +102,23 @@ 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__
@ -85,14 +127,15 @@ void FileEntry::setName(const char *name)
unsigned length = ValidName(name);
if (!length)
throw ProFUSE::ProDOSException(__METHOD__ ": Invalid file name.", ProFUSE::badPathSyntax);
throw ProDOS::Exception(__METHOD__ ": Invalid file name.", ProDOS::badPathSyntax);
_fileNameLength = length;
for (unsigned i = 0; i < length; ++i)
_fileName[i] = std::toupper(name[i]);
// not sure if this is a good idea or not.
//_modification = Date::Today();
// parent's responsibility.
//parent()->writeEntry(this);
}
unsigned FileEntry::fileSize()
@ -138,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;
@ -160,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);
@ -174,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);
@ -189,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;
@ -201,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;
@ -222,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();
@ -330,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;
@ -388,12 +697,14 @@ bool FileEntry::Compress(std::string& text)
if (count < 3) return false;
count = std::max((int)count, 255 - 32);
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;
}
@ -417,6 +728,7 @@ bool FileEntry::Uncompress(std::string& text)
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

View File

@ -1,10 +1,13 @@
#include <memory>
#include <algorithm>
#include <cerrno>
#include <Pascal/File.h>
#include <Pascal/Pascal.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>
@ -12,14 +15,41 @@
#include <Device/BlockDevice.h>
#include <Cache/BlockCache.h>
#pragma mark -
#pragma mark VolumeEntry
using namespace LittleEndian;
using namespace Pascal;
using namespace Device;
enum {
kMaxFiles = 77
};
// djb hash, case insensitive.
static unsigned NameHash(const char *cp)
{
unsigned hash = 5381;
unsigned c;
while ((c = *cp++))
{
c = std::toupper(c);
hash = ((hash << 5) + hash) + c;
}
return hash & 0x7FFFFFFF;
}
static bool NameEqual(const char *a, const char *b)
{
return ::strcasecmp(a, b) == 0;
}
unsigned VolumeEntry::ValidName(const char *cp)
{
// 7 chars max. Legal values: ascii, printable,
@ -29,6 +59,31 @@ unsigned VolumeEntry::ValidName(const char *cp)
return Entry::ValidName(cp, 7);
}
VolumeEntryPointer VolumeEntry::Open(Device::BlockDevicePointer device)
{
VolumeEntryPointer ptr;
//ptr = new VolumeEntry(device));
ptr = MAKE_SHARED(VolumeEntry, device);
// set up the weak references from the file entry to this.
if (ptr) ptr->setParents();
return ptr;
}
VolumeEntryPointer VolumeEntry::Create(Device::BlockDevicePointer device, const char *name)
{
VolumeEntryPointer ptr;
//ptr = new VolumeEntry(device, name);
ptr = MAKE_SHARED(VolumeEntry, device, name);
return ptr;
}
VolumeEntry::VolumeEntry()
{
_fileNameLength = 0;
@ -39,21 +94,28 @@ VolumeEntry::VolumeEntry()
setInode(1);
_inodeGenerator = 1;
_cache = NULL;
_device = NULL;
_inodeGenerator = 1;
}
VolumeEntry::VolumeEntry(const char *name, Device::BlockDevice *device)
VolumeEntry::VolumeEntry(Device::BlockDevicePointer device, const char *name) :
_device(device)
{
#undef __METHOD__
#define __METHOD__ "VolumeEntry::VolumeEntry"
unsigned length;
unsigned deviceBlocks = device->blocks();
deviceBlocks = std::min(0xffffu, deviceBlocks);
if (deviceBlocks < 6)
throw ::Exception(__METHOD__ ": device too small.");
length = ValidName(name);
if (!length)
throw ProFUSE::Exception(__METHOD__ ": Invalid volume name.");
throw ProDOS::Exception(__METHOD__ ": Invalid volume name.", ProDOS::badPathSyntax);
_firstBlock = 0;
_lastBlock = 6;
@ -69,13 +131,13 @@ VolumeEntry::VolumeEntry(const char *name, Device::BlockDevice *device)
_fileName[i] = std::toupper(name[i]);
}
_lastVolumeBlock = device->blocks();
_lastVolumeBlock = deviceBlocks;
_fileCount = 0;
_accessTime = 0;
_lastBoot = Date::Today();
_cache = BlockCache::Create(device);
_device = device;
_cache = BlockCache::Create(device);
_address = 512 * 2;
@ -96,10 +158,11 @@ VolumeEntry::VolumeEntry(const char *name, Device::BlockDevice *device)
}
VolumeEntry::VolumeEntry(Device::BlockDevice *device)
VolumeEntry::VolumeEntry(Device::BlockDevicePointer device)
{
unsigned blockCount;
ProFUSE::auto_array<uint8_t> buffer(new uint8_t[512]);
//unsigned deviceBlocks = device->blocks();
::auto_array<uint8_t> buffer(new uint8_t[512]);
// read the header block, then load up all the header
@ -126,54 +189,79 @@ VolumeEntry::VolumeEntry(Device::BlockDevice *device)
}
// now load up all the children.
// if this throws, memory could be lost...
// the parent cannot be set (yet), since we need a shared_ptr to create a weak_ptr.
try
{
for (unsigned i = 1; i <= _fileCount; ++i)
{
std::auto_ptr<FileEntry> child;
//
child.reset(new FileEntry(buffer.get() + i * 0x1a));
child->setInode(++_inodeGenerator);
child->_parent = this;
child->_address = 512 * 2 + i * 0x1a;
_files.push_back(child.release());
}
}
catch (...)
std::vector<FileEntryPointer>::iterator iter;
unsigned block;
for (unsigned i = 1; i <= _fileCount; ++i)
{
std::vector<FileEntry *>::iterator iter;
for(iter = _files.begin(); iter != _files.end(); ++iter)
{
if (*iter) delete *iter;
}
throw;
FileEntryPointer child;
//
child = FileEntry::Open(buffer.get() + i * 0x1a);
child->setInode(++_inodeGenerator);
// need to set later....
//child->_parent = this;
child->_address = 512 * 2 + i * 0x1a;
_files.push_back(child);
}
// sanity check _firstBlock, _lastBlock?
block = _lastBlock;
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
FileEntryPointer e = *iter;
bool error = false;
if (e->_firstBlock > e->_lastBlock)
error = true;
if (e->_firstBlock >= _lastVolumeBlock)
error = true;
if (e->_lastBlock > _lastVolumeBlock)
error = true;
if (e->_firstBlock < block)
error = true;
if (error)
throw ProDOS::Exception(__METHOD__ ": Invalid file entry.", ProDOS::dirError);
block = e->_lastBlock;
}
calcMaxFileSize();
}
VolumeEntry::~VolumeEntry()
{
std::vector<FileEntry *>::iterator iter;
for(iter = _files.begin(); iter != _files.end(); ++iter)
{
if (*iter) delete *iter;
}
delete _cache;
// _device is deleted by _cache.
//delete _device;
}
void VolumeEntry::setParents()
{
// parent is this....
VolumeEntryWeakPointer parent(thisPointer());
std::vector<FileEntryPointer>::iterator iter;
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
FileEntryPointer e = *iter;
e->_parent = parent;
}
}
void VolumeEntry::init(void *vp)
{
#undef __METHOD__
@ -184,7 +272,7 @@ void VolumeEntry::init(void *vp)
// verify filenamelength <= 7
if (_fileNameLength > 7)
throw new ProFUSE::Exception(__METHOD__ ": invalid name length");
throw ProDOS::Exception(__METHOD__ ": invalid name length", ProDOS::badPathSyntax);
// verify fileKind == 0
// verify _fileCount reasonable
@ -204,53 +292,80 @@ void VolumeEntry::init(void *vp)
}
FileEntry *VolumeEntry::fileAtIndex(unsigned i) const
FileEntryPointer VolumeEntry::fileAtIndex(unsigned i) const
{
return i < _files.size() ? _files[i] : NULL;
return i < _files.size() ? _files[i] : FileEntryPointer();
}
FileEntry *VolumeEntry::fileByName(const char *name) const
FileEntryPointer VolumeEntry::fileByName(const char *name) const
{
std::vector<FileEntry *>::const_iterator iter;
std::vector<FileEntryPointer>::const_iterator iter;
for(iter = _files.begin(); iter != _files.end(); ++iter)
{
FileEntry *e = *iter;
FileEntryPointer e = *iter;
if (::strcasecmp(name, e->name()) == 0) return e;
}
return NULL;
return FileEntryPointer();
}
unsigned VolumeEntry::unlink(const char *name)
/*
* Remove a file.
* Returns 0 on succes, -1 (and errno) on failure.
* May also throw a device error.
*/
int VolumeEntry::unlink(const char *name)
{
unsigned index;
if (_device->readOnly()) return ProFUSE::drvrWrtProt; // WRITE-PROTECTED DISK
for(index = 0; index < _fileCount; ++index)
std::vector<FileEntryPointer>::iterator iter;
if (_device->readOnly())
{
FileEntry *e = _files[index];
errno = EROFS;
return -1;
}
// TODO -- consider having an unordered_map of file names.
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
FileEntryPointer e = *iter;
if (::strcasecmp(name, e->name()) == 0)
{
delete e;
_files[index] = NULL;
// if not the first entry, update the previous entry's
// _maxFileSize.
if (iter != _files.begin())
{
FileEntryPointer prev = iter[-1];
prev->_maxFileSize += e->_maxFileSize;
}
iter->reset();
break;
}
}
if (index == _fileCount) return ProFUSE::fileNotFound; // FILE NOT FOUND
if (iter == _files.end())
{
errno = ENOENT;
return -1;
}
_files.erase(_files.begin() + index);
_files.erase(iter);
_fileCount--;
index = distance(_files.begin(), iter);
// reset addresses.
for (unsigned i = index; i < _fileCount; ++i)
for ( ; iter != _files.end(); ++iter)
{
FileEntry *e = _files[i];
FileEntryPointer e = *iter;
e->_address -= 0x1a;
}
// need to update the header blocks.
ProFUSE::auto_array<uint8_t> buffer(readDirectoryHeader());
::auto_array<uint8_t> buffer(readDirectoryHeader());
// update the filecount.
@ -271,48 +386,341 @@ unsigned VolumeEntry::unlink(const char *name)
}
unsigned VolumeEntry::rename(const char *oldName, const char *newName)
/*
* Renames a file, removing any other file with newName (if present)
* Returns 0 on success, -1 on error (and sets errno)
* May also throw an error of some sort.
*/
int VolumeEntry::rename(const char *oldName, const char *newName)
{
FileEntry *e;
FileEntryPointer e;
// 1. verify old name exists.
// check if read only.
if (_device->readOnly())
{
errno = EROFS;
return -1;
}
// verify file exists.
e = fileByName(oldName);
if (!e)
return ProFUSE::fileNotFound;
{
errno = ENOENT;
return -1;
}
// 2. verify new name is valid
// verify new name is valid.
if (!FileEntry::ValidName(newName))
return ProFUSE::badPathSyntax;
{
errno = EINVAL; // invalid name, at least.
return -1;
}
// delete the new file
if (unlink(newName) != 0)
{
if (errno != ENOENT) return -1;
}
// 3. verify new name does not exist.
if (fileByName(newName))
return ProFUSE::dupPathName;
// 4. set the name (throws on error, which won't happen since
// we already verified the name is valid.
// update with the new name.
e->setName(newName);
// and commit to disk.
// 5. write to disk.
writeEntry(e);
writeEntry(e.get());
_cache->sync();
return 0;
}
/*
* copy a file (here to simplify copying text files).
* if newName exists, delete it.
*
*/
int VolumeEntry::copy(const char *oldName, const char *newName)
{
FileEntryPointer oldEntry;
FileEntryPointer newEntry;
// check if read only.
if (_device->readOnly())
{
errno = EROFS;
return -1;
}
// verify file exists.
oldEntry = fileByName(oldName);
if (!oldEntry)
{
errno = ENOENT;
return -1;
}
// verify new name is valid.
if (!FileEntry::ValidName(newName))
{
errno = EINVAL; // invalid name, at least.
return -1;
}
// if newName does not exist, call create w/ block size and write everything.
// if newName does exist, overwrite it if it will fit. Otherwise, remove it, create a file, etc.
unsigned blocks = oldEntry->blocks();
newEntry = fileByName(newName);
if (newEntry)
{
// if newEntry is large enough, overwrite it.
if (newEntry->_maxFileSize >= blocks * 512)
{
if (newEntry->_pageSize)
{
delete newEntry->_pageSize;
newEntry->_pageSize = NULL;
newEntry->_fileSize = 0;
}
}
else
{
if (maxContiguousBlocks() < blocks)
{
errno = ENOSPC;
return -1;
}
newEntry.reset();
if (unlink(newName) != 0) return -1;
}
}
if (!newEntry)
{
// newName does not exist (or was just deleted), so create a new file (if possible) and write to it.
if (maxContiguousBlocks() < blocks)
{
errno = ENOSPC;
return -1;
}
newEntry = create(newName, blocks);
if (!newEntry) return -1; // create() sets errno.
}
unsigned VolumeEntry::krunch()
newEntry->_fileKind = oldEntry->fileKind();
newEntry->_lastByte = oldEntry->lastByte();
newEntry->_lastBlock = newEntry->firstBlock() + blocks;
newEntry->_modification = Date::Today();
for (unsigned i = 0; i < blocks; i++)
{
void *src = loadBlock(oldEntry->firstBlock() + i);
_cache->write(newEntry->firstBlock() + i, src);
unloadBlock(oldEntry->firstBlock() + i, false);
}
_cache->sync();
return 0;
}
/*
* create a file. if blocks is defined, verifies the file could
* expand to fit.
* returns FileEntry on success, NULL (and errno) on failure.
*
*/
/*
* TODO -- if blocks is defined, try to fit in a gap rather than putting at the end.
*
*
*
*/
FileEntryPointer VolumeEntry::create(const char *name, unsigned blocks)
{
// 0. check read only access.
// 1. verify < 77 file names.
// 2. verify space at end of disk.
// 3. make sure it's a legal file name.
// 4. verify it's not a duplicate file name.
// 6. create the file entry.
// 7. insert into _files, write to disk, update _maxFileSize
unsigned lastBlock = _lastBlock;
FileEntryPointer entry;
FileEntryPointer prev;
FileEntryPointer curr;
std::vector<FileEntryPointer>::iterator iter;
if (readOnly())
{
errno = EROFS;
return FileEntryPointer();
}
if (_fileCount == kMaxFiles)
{
errno = ENOSPC;
return FileEntryPointer();
}
if (!FileEntry::ValidName(name))
{
errno = EINVAL;
return FileEntryPointer();
}
if (fileByName(name))
{
errno = EEXIST;
return FileEntryPointer();
}
entry = FileEntry::Create(name, kUntypedFile);
::auto_array<uint8_t> buffer(readDirectoryHeader());
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
FileEntryPointer e = *iter;
unsigned freeSpace = e->_firstBlock - lastBlock;
// this could do something stupid like selecting a slot with only 1 free block but too bad.
if (freeSpace < blocks)
{
lastBlock = e->_lastBlock;
prev = e;
continue;
}
// update previous entry max file size.
if (prev)
{
prev->_maxFileSize = prev->blocks() * 512;
}
// insert() inserts an item *before* the current item and returns an iterator to the
// element inserted.
// afterwards, iter will point to curr+1
// keep track of the index *before* the insert.
unsigned index = distance(_files.begin(), iter); // current index.
iter = _files.insert(iter, entry);
++_fileCount;
curr = entry;
//curr->_parent = this;
curr->_parent = VolumeEntryWeakPointer(thisPointer());
curr->_firstBlock = lastBlock;
curr->_lastBlock = lastBlock + 1;
curr->_lastByte = 0;
curr->_maxFileSize = freeSpace * 512;
//curr->_address = 2 * 512 + 0x1a + 0x1a * index;
// move all entries after this one up by 0x1a bytes, then write this header.
unsigned count = _fileCount - index - 1;
unsigned offset = index * 0x1a + 0x1a;
std::memmove(buffer.get() + offset + 0x1a, buffer.get() + offset, count * 0x1a);
break;
}
if (iter == _files.end())
{
// check if we can append
unsigned freeSpace = _lastVolumeBlock - lastBlock;
if (freeSpace < blocks)
{
errno = ENOSPC;
return FileEntryPointer();
}
_files.push_back(entry);
_fileCount++;
curr = entry;
//curr->_parent = this;
curr->_parent = VolumeEntryWeakPointer(thisPointer());
curr->_firstBlock = lastBlock;
curr->_lastBlock = lastBlock + 1;
curr->_lastByte = 0;
curr->_maxFileSize = freeSpace * 512;
curr->_address = 2 * 512 + 0x1a * _fileCount; // s/b +1 since entry 0 is the header.
if (prev)
{
prev->_maxFileSize = prev->blocks() * 512;
}
writeEntry(curr.get());
writeEntry(); // header.
}
else
{
// update all addresses to make life easier.
unsigned address = 2 * 512 + 0x1a;
for (iter = _files.begin(); iter != _files.end(); ++iter, address += 0x1a)
{
FileEntryPointer e = *iter;
e->_address = address;
}
IOBuffer b(buffer.get(), 4 * 512);
writeDirectoryEntry(&b);
b.setOffset(curr->_address - 2 * 512);
curr->writeDirectoryEntry(&b);
writeDirectoryHeader(buffer.get());
}
_cache->sync();
return curr;
}
/*
* TODO -- consider trying to move files from the end to fill gaps
* if it would reduce the number of blocks that need to be re-arranged.
*
* build a list of gaps, reverse iterate and move last file to first gap
* only 77 files, so can consider all options.
*
*
*
*
*
*/
int VolumeEntry::krunch()
{
unsigned prevBlock;
std::vector<FileEntry *>::const_iterator iter;
std::vector<FileEntryPointer>::const_iterator iter;
bool gap = false;
@ -322,7 +730,7 @@ unsigned VolumeEntry::krunch()
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
FileEntry *e = *iter;
FileEntryPointer e = *iter;
unsigned first = e->firstBlock();
unsigned last = e->lastBlock();
@ -330,13 +738,13 @@ unsigned VolumeEntry::krunch()
if (first != prevBlock) gap = true;
if (first < prevBlock)
return ProFUSE::damagedBitMap;
return ProDOS::damagedBitMap;
if (last < first)
return ProFUSE::damagedBitMap;
return ProDOS::damagedBitMap;
if (first < volumeBlocks())
return ProFUSE::damagedBitMap;
return ProDOS::damagedBitMap;
prevBlock = last;
@ -348,7 +756,7 @@ unsigned VolumeEntry::krunch()
// need to update the header blocks.
ProFUSE::auto_array<uint8_t> buffer(readDirectoryHeader());
::auto_array<uint8_t> buffer(readDirectoryHeader());
IOBuffer b(buffer.get(), 512 * blocks());
@ -357,7 +765,7 @@ unsigned VolumeEntry::krunch()
unsigned offset = 0;
for (iter = _files.begin(); iter != _files.end(); ++iter, ++offset)
{
FileEntry *e = *iter;
FileEntryPointer e = *iter;
b.setOffset(0x1a + 0x1a * offset);
@ -396,19 +804,98 @@ unsigned VolumeEntry::krunch()
_cache->sync();
calcMaxFileSize();
return 0;
}
/*
* return the number of free blocks.
* if krunched is true, returns sum of all free blocks
* if krunched is false, returns free blocks at end.
*
*/
unsigned VolumeEntry::freeBlocks(bool krunched) const
{
unsigned freeBlocks = 0;
unsigned lastBlock = 0;
if (krunched)
{
std::vector<FileEntryPointer>::const_iterator iter;
lastBlock = _lastBlock;
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
const FileEntryPointer e = *iter;
freeBlocks += e->_firstBlock - lastBlock;
lastBlock = e->_lastBlock;
}
}
else
{
lastBlock = _fileCount ? _files.back()->_lastBlock : _lastBlock;
}
freeBlocks += _lastVolumeBlock - lastBlock;
return freeBlocks;
}
/*
* return true if there are any gaps, false otherwise.
*
*
*/
bool VolumeEntry::canKrunch() const
{
std::vector<FileEntryPointer>::const_iterator iter;
unsigned lastBlock = _lastBlock;
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
const FileEntryPointer e = *iter;
if (e->_lastBlock != lastBlock) return true;
lastBlock = e->_lastBlock;
}
return false;
}
/*
* returns the largest free block.
*
*/
unsigned VolumeEntry::maxContiguousBlocks() const
{
unsigned max = 0;
std::vector<FileEntryPointer>::const_iterator iter;
unsigned lastBlock = _lastBlock;
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
const FileEntryPointer e = *iter;
unsigned free = e->_firstBlock - lastBlock;
max = std::max(max, free);
lastBlock = e->_lastBlock;
}
max = std::max(max, _lastVolumeBlock - lastBlock);
return max;
}
@ -467,7 +954,7 @@ void VolumeEntry::writeDirectoryHeader(void *buffer)
uint8_t *VolumeEntry::readBlocks(unsigned startingBlock, unsigned count)
{
ProFUSE::auto_array<uint8_t> buffer(new uint8_t[512 * count]);
::auto_array<uint8_t> buffer(new uint8_t[512 * count]);
for (unsigned i = 0; i < count; ++i)
_cache->read(startingBlock + i, buffer.get() + 512 * i);
@ -483,6 +970,14 @@ void VolumeEntry::writeBlocks(void *buffer, unsigned startingBlock, unsigned cou
}
// write directory entry, does not sync.
void VolumeEntry::writeEntry()
{
IOBuffer iob(loadBlock(2),512);
writeDirectoryEntry(&iob);
unloadBlock(2, true);
}
// does not sync.
void VolumeEntry::writeEntry(FileEntry *e)
{
@ -504,7 +999,7 @@ void VolumeEntry::writeEntry(FileEntry *e)
else
{
// crosses page boundaries.
ProFUSE::auto_array<uint8_t> buffer(readBlocks(startBlock, 2));
::auto_array<uint8_t> buffer(readBlocks(startBlock, 2));
IOBuffer b(buffer.get() + offset, 0x1a);
@ -512,6 +1007,19 @@ void VolumeEntry::writeEntry(FileEntry *e)
writeBlocks(buffer.get(), startBlock, 2);
}
}
}
// set _maxFileSize for all entries.
void VolumeEntry::calcMaxFileSize()
{
std::vector<FileEntryPointer>::reverse_iterator riter;
unsigned block = _lastVolumeBlock;
for (riter = _files.rbegin(); riter != _files.rend(); ++riter)
{
FileEntryPointer e = *riter;
e->_maxFileSize = (block - e->_firstBlock) * 512;
block = e->_firstBlock;
}
}

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 "";
}
}

View File

@ -1,10 +1,9 @@
#ifndef __EXCEPTION_H__
#define __EXCEPTION_H__
#ifndef __PRODOS_EXCEPTION_H__
#define __PRODOS_EXCEPTION_H__
#include <string>
#include <exception>
#include <Common/Exception.h>
namespace ProFUSE {
namespace ProDOS {
// ProDOS Errors
@ -12,7 +11,7 @@ enum
{
badSystemCall = 0x01,
invalidPcount = 0x04,
gsosActice = 0x07,
gsosActive = 0x07,
devNotFound = 0x10,
invalidDevNum = 0x11,
drvrBadReq = 0x20,
@ -67,82 +66,27 @@ enum
resAddErr = 0x71
};
class Exception : public std::exception
{
class Exception : public ::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);
virtual const char *errorString();
private:
int _error;
std::string _string;
typedef ::Exception super;
};
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 char *cp, int error) :
super(cp, error)
{
}
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)
inline Exception::Exception(const std::string& string, int error) :
super(string, error)
{
}
@ -150,4 +94,4 @@ inline ProDOSException::ProDOSException(const std::string& string, int error) :
}
#endif
#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

View File

@ -231,8 +231,6 @@
<key>Layout</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXBottomSmartGroupGIDs</key>
@ -269,14 +267,21 @@
<key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
<array>
<string>08FB7794FE84155DC02AAC07</string>
<string>B6272A8D11D5AC3C0073C73A</string>
<string>B6272AA411D5AE0C0073C73A</string>
<string>B63EFBE711A244EE00C90DCE</string>
<string>B63EFACF11A09DB500C90DCE</string>
<string>B63EFAB511A098C400C90DCE</string>
<string>B63EFA9411A094E000C90DCE</string>
<string>B656ADED11A84D3200AB578A</string>
<string>1C37FBAC04509CD000000102</string>
<string>1C37FAAC04509CD000000102</string>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>2</integer>
<integer>0</integer>
<integer>63</integer>
<integer>62</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
@ -297,7 +302,7 @@
<real>318</real>
</array>
<key>RubberWindowFrame</key>
<string>394 69 1239 1000 0 0 1920 1178 </string>
<string>413 92 1239 1000 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXSmartGroupTreeModule</string>
@ -313,7 +318,7 @@
<key>PBXProjectModuleGUID</key>
<string>B63EFA6111A093C200C90DCE</string>
<key>PBXProjectModuleLabel</key>
<string>NibbleTest.cpp</string>
<string>VolumeEntry.cpp</string>
<key>PBXSplitModuleInNavigatorKey</key>
<dict>
<key>Split0</key>
@ -321,19 +326,15 @@
<key>PBXProjectModuleGUID</key>
<string>B63EFA6211A093C200C90DCE</string>
<key>PBXProjectModuleLabel</key>
<string>NibbleTest.cpp</string>
<string>VolumeEntry.cpp</string>
<key>_historyCapacity</key>
<integer>0</integer>
<key>bookmark</key>
<string>B6F3647211AB35FE0045E114</string>
<string>B6F8D4AD131CB30300461B54</string>
<key>history</key>
<array>
<string>B63EFC2611A2D5A400C90DCE</string>
<string>B63EFC2B11A2D5A400C90DCE</string>
<string>B63EFC3211A2D5A400C90DCE</string>
<string>B63EFCB211A3639400C90DCE</string>
<string>B63EFCB611A3639400C90DCE</string>
<string>B63EFD7F11A4436B00C90DCE</string>
<string>B63EFDA011A4488200C90DCE</string>
<string>B63EFDA511A4488200C90DCE</string>
<string>B63EFDA711A4488200C90DCE</string>
@ -343,37 +344,56 @@
<string>B63EFDAF11A4488200C90DCE</string>
<string>B63EFDB111A4488200C90DCE</string>
<string>B63EFDB311A4488200C90DCE</string>
<string>B63EFDB711A4488200C90DCE</string>
<string>B63EFDC711A4494C00C90DCE</string>
<string>B63EFDE611A44E9300C90DCE</string>
<string>B63EFDF211A4504200C90DCE</string>
<string>B63EFEA011A488C200C90DCE</string>
<string>B63EFEA111A488C200C90DCE</string>
<string>B63EFEA211A488C200C90DCE</string>
<string>B63EFEA311A488C200C90DCE</string>
<string>B63EFEA711A488C200C90DCE</string>
<string>B63EFEA811A488C200C90DCE</string>
<string>B63EFEA911A488C200C90DCE</string>
<string>B63EFEEC11A4A70400C90DCE</string>
<string>B6E5F0DD11A60726000AD141</string>
<string>B6E5F0F211A73144000AD141</string>
<string>B6E5F11211A73340000AD141</string>
<string>B6E5F13A11A74F2B000AD141</string>
<string>B6E5F18111A76B74000AD141</string>
<string>B6E5F18211A76B74000AD141</string>
<string>B656AE2311A84FA400AB578A</string>
<string>B656AE2411A84FA400AB578A</string>
<string>B656AE2611A84FA400AB578A</string>
<string>B656AE2711A84FA400AB578A</string>
<string>B656AE6511A8591F00AB578A</string>
<string>B656AEA711A9C91600AB578A</string>
<string>B656AEA811A9C91600AB578A</string>
<string>B656AF2711AA10C900AB578A</string>
<string>B656AF2811AA10C900AB578A</string>
<string>B656AF2A11AA10C900AB578A</string>
<string>B6F3642D11AA1E070045E114</string>
<string>B6F3647111AB35FE0045E114</string>
<string>B6F3647011AB35F00045E114</string>
<string>B6F3649E11AB59190045E114</string>
<string>B6F364B711AB84840045E114</string>
<string>B6F3652E11ADD5D20045E114</string>
<string>B6F3653011ADD5D20045E114</string>
<string>B6F3657E11AE255B0045E114</string>
<string>B6F3665411B190370045E114</string>
<string>B6F3665C11B194AC0045E114</string>
<string>B6F366C411B1A28C0045E114</string>
<string>B6F366C611B1A28C0045E114</string>
<string>B6F366C711B1A28C0045E114</string>
<string>B6F366C811B1A28C0045E114</string>
<string>B6F366C911B1A28C0045E114</string>
<string>B6F366CA11B1A28C0045E114</string>
<string>B6F366CB11B1A28C0045E114</string>
<string>B6F3677C11B326580045E114</string>
<string>B6F3677D11B326580045E114</string>
<string>B6F3677E11B326580045E114</string>
<string>B6F367A011B330D10045E114</string>
<string>B6F367AE11B337A70045E114</string>
<string>B6F3684A11B44D090045E114</string>
<string>B65CFB6911B495790024A2D9</string>
<string>B6272A9911D5AC5E0073C73A</string>
<string>B6272A9B11D5AC5E0073C73A</string>
<string>B6272A9C11D5AC5E0073C73A</string>
<string>B6272B0D11D691320073C73A</string>
<string>B6272B0E11D691320073C73A</string>
<string>B6272B0F11D691320073C73A</string>
<string>B6272B1011D691320073C73A</string>
<string>B6272B1111D691320073C73A</string>
<string>B6272B1311D691320073C73A</string>
<string>B6272B1411D691320073C73A</string>
<string>B6272B1611D691320073C73A</string>
<string>B6AA3AB21318D063007D4BA1</string>
<string>B6AA3AB31318D063007D4BA1</string>
<string>B6AA3AB41318D063007D4BA1</string>
<string>B6A53B8E1319DDFC00C9070F</string>
<string>B6A53BC7131B400800C9070F</string>
<string>B6A53BC8131B400800C9070F</string>
<string>B6A53C34131B4CF800C9070F</string>
</array>
</dict>
<key>SplitCount</key>
@ -387,21 +407,23 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {899, 562}}</string>
<string>{{0, 0}, {899, 497}}</string>
<key>RubberWindowFrame</key>
<string>394 69 1239 1000 0 0 1920 1178 </string>
<string>413 92 1239 1000 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>562pt</string>
<string>497pt</string>
</dict>
<dict>
<key>Proportion</key>
<string>392pt</string>
<string>457pt</string>
<key>Tabs</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
@ -412,9 +434,9 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{10, 27}, {899, 365}}</string>
<string>{{10, 27}, {899, 430}}</string>
<key>RubberWindowFrame</key>
<string>394 69 1239 1000 0 0 1920 1178 </string>
<string>413 92 1239 1000 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>XCDetailModule</string>
@ -430,7 +452,7 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{10, 27}, {899, 341}}</string>
<string>{{10, 27}, {899, 383}}</string>
</dict>
<key>Module</key>
<string>PBXProjectFindModule</string>
@ -468,7 +490,7 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{10, 27}, {899, 349}}</string>
<string>{{10, 27}, {899, 420}}</string>
</dict>
<key>Module</key>
<string>PBXBuildResultsModule</string>
@ -496,11 +518,11 @@
</array>
<key>TableOfContents</key>
<array>
<string>B6F3647311AB35FE0045E114</string>
<string>B6F8D48A131C605200461B54</string>
<string>1CA23ED40692098700951B8B</string>
<string>B6F3647411AB35FE0045E114</string>
<string>B6F8D48B131C605200461B54</string>
<string>B63EFA6111A093C200C90DCE</string>
<string>B6F3647511AB35FE0045E114</string>
<string>B6F8D48C131C605200461B54</string>
<string>1CA23EDF0692099D00951B8B</string>
<string>1CA23EE00692099D00951B8B</string>
<string>1CA23EE10692099D00951B8B</string>
@ -649,14 +671,14 @@
</array>
<key>TableOfContents</key>
<array>
<string>B6F3647611AB35FE0045E114</string>
<string>B6A53BCB131B400800C9070F</string>
<string>1CCC7628064C1048000F2A68</string>
<string>1CCC7629064C1048000F2A68</string>
<string>B6F3647711AB35FE0045E114</string>
<string>B6F3647811AB35FE0045E114</string>
<string>B6F3647911AB35FE0045E114</string>
<string>B6F3647A11AB35FE0045E114</string>
<string>B63EFA6111A093C200C90DCE</string>
<string>B6A53BCC131B400800C9070F</string>
<string>B6A53BCD131B400800C9070F</string>
<string>B6A53BCE131B400800C9070F</string>
<string>B6A53BCF131B400800C9070F</string>
<string>B6A53BD0131B400800C9070F</string>
</array>
<key>ToolbarConfigUserDefaultsMinorVersion</key>
<string>2</string>
@ -693,7 +715,7 @@
<string>/Users/kelvin/Projects/PROFuseX/PROFuseX.xcodeproj</string>
</array>
<key>WindowString</key>
<string>394 69 1239 1000 0 0 1920 1178 </string>
<string>413 92 1239 1000 0 0 1920 1178 </string>
<key>WindowToolsV3</key>
<array>
<dict>

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,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.

383
apfm.cpp
View File

@ -1,383 +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>
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 (lastBlock != volumeSize)
{
unsigned size = volumeSize - lastBlock;
max = std::max(max, size);
if (extended)
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.\n");
return 1;
}
for (unsigned i = 1; 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)
{
// cp src dest
// first character of ':' indicates pascal file, otherwise, is native file?
return 0;
}
int action_mv(int argc, char **argv, Pascal::VolumeEntry *volume)
{
// mv src dest
// first character of ':' indicates pascal file, otherwise is native file?
return 0;
}
int action_rm(int argc, char **argv, Pascal::VolumeEntry *volume)
{
// mv rm file [file ....]
// needs ':' as f
return 0;
}
int action_krunch(int argc, char **argv, Pascal::VolumeEntry *volume)
{
// compress file to remove gaps.
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;
#ifdef __linux__
putenv((char *)"POSIXLY_CORRECT=1"); // fix getopt to not mutate
#endif
/*
char *argv2[] = {
(char *)"afpm",
//(char *)"/Users/kelvin/Desktop/ucsd/pascal.dsk",
(char *)"/Users/kelvin/Desktop/ucsd/UCSD Pascal 1.2_3.DSK",
(char *)"cat",
(char *)"SPIRODEMO.TEXT",
NULL
};
char **argv = argv2;
int argc = 4;
*/
// 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::BlockDevice::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;
#ifdef __linux__
optind = 0;
#else
optreset = 1;
optind = 1;
#endif
if (argc < 2)
{
usage();
return 0;
}
const char *file = argv[0];
const char *action = argv[1];
try {
device.reset( Device::BlockDevice::Open(file, true, fmt) );
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,24 +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>
std::string fDiskImage;
void usage()
{
std::printf("profuse_pascal 0.1\n\n");
@ -42,6 +43,9 @@ void usage()
" 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"
@ -167,7 +171,7 @@ int main(int argc, char **argv)
int multithread = false;
std::auto_ptr<Pascal::VolumeEntry> volume;
Pascal::VolumeEntryPointer volume;
init_ops(&pascal_ops);
@ -191,15 +195,12 @@ int main(int argc, char **argv)
std::fprintf(stderr, "Warning: Unknown image type ``%s''\n", options.format);
}
bool readOnly = true;
try
{
{
Device::BlockDevicePointer device;
std::auto_ptr<Device::BlockDevice> device;
device.reset( Device::BlockDevice::Open(fDiskImage.c_str(), readOnly, format) );
device = Device::BlockDevice::Open(fDiskImage.c_str(), File::ReadOnly, format);
if (!device.get())
@ -208,20 +209,15 @@ int main(int argc, char **argv)
exit(1);
}
volume.reset( 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;
}

View File

@ -1,10 +1,4 @@
#ifdef __APPLE__
#define __FreeBSD__ 10
#define __DARWIN_64_BIT_INO_T 1
#endif
#define _FILE_OFFSET_BITS 64
#define FUSE_USE_VERSION 27
#include <string>
@ -13,18 +7,22 @@
#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() \
{ \
@ -56,20 +54,30 @@
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)
{
DEBUGNAME()
@ -82,7 +90,7 @@ static void pascal_init(void *userdata, struct fuse_conn_info *conn)
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
{
FileEntry *child = volume->fileAtIndex(i);
FileEntryPointer child = volume->fileAtIndex(i);
child->fileSize();
}
@ -96,6 +104,34 @@ static void pascal_destroy(void *userdata)
}
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
@ -105,7 +141,7 @@ static void pascal_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
DEBUGNAME()
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file;
FileEntryPointer file;
std::string attr;
unsigned attrSize;
@ -149,7 +185,7 @@ static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, si
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file;
FileEntryPointer file;
std::string attr(name);
ERROR(ino == 1, ENOATTR)
@ -223,7 +259,7 @@ static void pascal_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t of
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();
@ -269,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.
@ -352,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;
@ -363,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;
}
@ -378,7 +414,7 @@ static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
struct stat st;
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file;
FileEntryPointer file;
if (ino == 1)
{
@ -392,7 +428,7 @@ static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info
ERROR(file == NULL, ENOENT)
//printf("\t%s\n", file->name());
stat(file, &st);
stat(file.get(), &st);
fuse_reply_attr(req, &st, 0.0);
}
@ -402,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)
{
unsigned index;
DEBUGNAME()
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
FileEntry *file;
FileEntryPointer file;
ERROR(ino == 1, EISDIR)
@ -415,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);
}
@ -423,31 +473,38 @@ 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)
{
unsigned index = fi->fh;
DEBUGNAME()
fd_table[index].reset();
fd_table_available.push_back(index);
fuse_reply_err(req, 0);
}
static void pascal_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
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());
@ -460,7 +517,6 @@ 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()
@ -469,6 +525,7 @@ void init_ops(fuse_lowlevel_ops *ops)
ops->init = pascal_init;
ops->destroy = pascal_destroy;
ops->statfs = pascal_statfs;
// returns pascal.filekind, text encoding.
ops->listxattr = pascal_listxattr;

View File

@ -1,12 +1,6 @@
#include <Device/BlockDevice.h>
#include <Device/RawDevice.h>
#include <ProFUSE/Exception.h>
#include <Pascal/File.h>
#include <memory>
#include <new>
#include <cstdio>
#include <cstring>
#include <cerrno>
@ -15,6 +9,22 @@
#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;
@ -108,11 +118,12 @@ void usage()
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"
std::printf(" -v volume_name Specify the volume name.\n"
" Default is Untitled.\n"
" -s size specify size in blocks.\n"
" -s size Specify size in blocks.\n"
" Default is 1600 blocks (800K)\n"
" -f format specify the disk image format. Valid values are:\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"
@ -128,6 +139,8 @@ 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;
@ -137,40 +150,43 @@ int main(int argc, char **argv)
{
switch(c)
{
case '?':
case 'h':
usage();
return 0;
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 'h':
default:
usage();
return c == 'h' ? 0 : 1;
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':
{
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;
}
}
@ -196,10 +212,10 @@ int main(int argc, char **argv)
{
struct stat st;
bool rawDevice;
bool rawDevice = false;
std::auto_ptr<BlockDevice> device;
std::auto_ptr<VolumeEntry> volume;
BlockDevicePointer device;
VolumeEntryPointer volume;
// Check for block device. if so, verify.
// if file exists, verify before overwrite.
@ -212,7 +228,7 @@ int main(int argc, char **argv)
fprintf(stderr, "`%s' is a raw device. Are you sure you want to initialize it? ", fname);
if (!yes_or_no()) return -1;
device.reset( RawDevice::Open(fname, false) );
device = RawDevice::Open(fname, File::ReadWrite);
blocks = device->blocks();
rawDevice = true;
@ -244,7 +260,7 @@ int main(int argc, char **argv)
}
if (!rawDevice)
device.reset( BlockDevice::Create(fname, volumeName.c_str(), blocks, format));
device = BlockDevice::Create(fname, volumeName.c_str(), blocks, format);
if (!device.get())
{
@ -253,39 +269,46 @@ int main(int argc, char **argv)
}
volume.reset(
new VolumeEntry(volumeName.c_str(), device.get())
);
device.release();
/*
ProFUSE::MappedFile bootBlock("pascal.bootblock", true);
if (bootBlock.fileSize() == 1024)
if (!bootFile.empty())
{
uint8_t buffer[512];
bootBlock.setBlocks(2);
MappedFile bf(bootFile.c_str(), File::ReadOnly, std::nothrow);
for (unsigned block = 0; block < 2; ++block)
if (!bf.isValid())
{
bootBlock.readBlock(block, buffer);
volume->writeBlock(block, buffer);
}
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 (ProFUSE::POSIXException& e)
catch (::Exception& e)
{
std::fprintf(stderr, "%s\n", e.what());
std::fprintf(stderr, "%s\n", ::strerror(e.error()));
return -2;
}
catch (ProFUSE::Exception& e)
{
std::fprintf(stderr, "%s\n", e.what());
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);
}

View File

@ -13,10 +13,27 @@
#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.
@ -36,6 +53,11 @@ inline int setxattr(const char *path, const char *name, void *value, size_t size
{
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)
@ -124,40 +146,48 @@ ssize_t get_attr_list(const char * fname, std::vector<std::string> &out)
}
/*
* 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 *file, const char *attr)
void dumpxattr(const char *fname, const char *attr_name)
{
ssize_t asize;
char *buffer;
std::vector<uint8_t> buffer;
if (read_xattr(fname, attr_name, buffer) < 0) {
warn("getxattr(\"%s\", \"%s\")", fname, attr_name);
return;
}
asize = getxattr(file, attr, NULL, 0);
if (asize < 0)
{
std::perror(attr);
return;
}
std::printf("%s: %u\n", attr, (unsigned)asize);
buffer = new char[asize];
//if (!buffer) return;
asize = getxattr(file, attr, buffer, asize);
if (asize >= 0)
{
hexdump((uint8_t *)buffer, asize);
}
else
{
std::perror(attr);
}
delete []buffer;
std::printf("%s: %u\n", attr_name, (unsigned)buffer.size());
hexdump(buffer.data(), buffer.size());
}
@ -165,7 +195,7 @@ char *buffer;
* list a file's attributes (and size)
*
*/
int list(int argc, char **argv)
int op_list(int argc, char **argv)
{
const char *fname = *argv;
@ -205,7 +235,7 @@ int list(int argc, char **argv)
}
else
{
std::perror(attr_name);
warn("getxattr(\"%s\", \"%s\")", fname, attr_name);
}
}
@ -217,7 +247,7 @@ int list(int argc, char **argv)
* hexdump a file's attributes.
*
*/
int dump(int argc, char **argv)
int op_dump(int argc, char **argv)
{
const char *fname = *argv;
@ -250,7 +280,7 @@ int dump(int argc, char **argv)
// must specify the xattr name.
int read(int argc, char **argv)
int op_read(int argc, char **argv)
{
if (argc != 2)
@ -261,37 +291,22 @@ int read(int argc, char **argv)
const char *fname = argv[0];
const char *attr_name = argv[1];
// get the attribute size and read it in.
ssize_t asize = getxattr(fname, attr_name, NULL, 0);
if (asize < 0)
{
std::perror(attr_name);
return -1;
}
if (asize == 0) return 0;
uint8_t *buffer = new uint8_t[asize];
asize = getxattr(fname, attr_name, buffer, asize);
if (asize < 0)
{
std::perror(attr_name);
return -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, asize) != asize)
if (::write(STDOUT_FILENO, buffer.data(), buffer.size()) < 0)
{
perror(NULL);
delete []buffer;
warn("write");
return -1;
}
delete []buffer;
return 0;
}
@ -303,51 +318,50 @@ ssize_t read_all(int fd, std::vector<uint8_t> &out)
// stdin could be a file, a pipe, or a tty.
if (fstat(fd, &st) < 0)
{
std::perror("stdin");
warn("fstat stdin");
return -1;
}
bsize = std::max(st.st_blksize, (blksize_t)512);
out.reserve(std::max(st.st_size, (off_t)512));
uint8_t *buffer = new uint8_t[bsize];
std::vector<uint8_t> buffer;
buffer.resize(bsize);
for(;;)
{
ssize_t size = ::read(fd, buffer, bsize);
ssize_t size = ::read(fd, buffer.data(), bsize);
if (size == 0) break;
if (size == -1)
{
if (errno == EINTR) continue;
delete[] buffer;
std::perror(NULL);
warn("read");
return -1;
}
// force reserve?
out.insert(out.end(), buffer, buffer + size);
out.insert(out.end(), buffer.data(), buffer.data() + size);
}
delete []buffer;
return out.size();
}
// xattr write filename attrname
// stdin -> filename:attrname
int write(int argc, char **argv)
int op_write(int argc, char **argv)
{
std::vector<uint8_t> buffer;
int flags = 0;
if (argc != 2)
{
std::fprintf(stderr, "Must specify attribute to be read.\n");
std::fprintf(stderr, "Must specify attribute to be written.\n");
return -1;
}
@ -365,15 +379,11 @@ int write(int argc, char **argv)
// if neither of the above is specified, will create and replace.
// XATTR_NOFOLLOW : do not follow symbolic links (Apple only)
#ifdef __APPLE__
flags = XATTR_NOFOLLOW;
#endif
ssize_t asize = setxattr(fname, attr_name, &buffer[0], buffer.size(), flags);
ssize_t asize = setxattr(fname, attr_name, buffer.data(), buffer.size(), 0);
if (asize < 0)
{
std::perror(attr_name);
warn("setxattr(\"%s\", \"%s\")", fname, attr_name);
return -1;
}
@ -381,6 +391,126 @@ int write(int argc, char **argv)
}
/*
* 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");
@ -401,12 +531,20 @@ int main(int argc, char **argv)
{
if (argc < 3) usage(*argv);
if (std::strcmp(argv[1], "list") == 0) return list(argc - 2, argv + 2);
if (std::strcmp(argv[1], "dump") == 0) return dump(argc - 2, argv + 2);
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 read(argc - 2, argv + 2);
if (std::strcmp(argv[1], "write") == 0) return write(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);

BIN
libNuFX.a Normal file

Binary file not shown.