1 #include "filesystem.hpp"
3 #include "serialization.hpp"
8 filesystem::filesystem(const std::string
& file
)
10 backing
.open(file
, std::ios_base::out
| std::ios_base::app
);
12 backing
.open(file
, std::ios_base::in
| std::ios_base::out
| std::ios_base::binary
| std::ios_base::ate
);
14 throw std::runtime_error("Can't open file '" + file
+ "'");
15 uint64_t backing_size
= backing
.tellp();
16 backing
.seekp(0, std::ios_base::beg
);
18 throw std::runtime_error("Can't get file size.");
19 supercluster_count
= (backing_size
+ SUPERCLUSTER_SIZE
- 1) / SUPERCLUSTER_SIZE
;
20 for(unsigned i
= 0; i
< supercluster_count
; i
++)
21 superclusters
[i
].load(backing
, i
);
22 if(supercluster_count
== 0) {
23 allocate_cluster(); //Will allocate cluster 2 (main directory).
24 //Write superblock to cluster 1.
25 char superblock
[CLUSTER_SIZE
];
26 memset(superblock
, 0, CLUSTER_SIZE
);
30 write_data(c
, p
, superblock
, CLUSTER_SIZE
, c2
, p2
);
31 strcpy(superblock
, "sefs-magic");
34 write_data(c
, p
, superblock
, CLUSTER_SIZE
, c2
, p2
);
36 //Read superblock from cluster 1.
37 char superblock
[CLUSTER_SIZE
];
40 read_data(c
, p
, superblock
, CLUSTER_SIZE
);
41 if(strcmp(superblock
, "sefs-magic"))
42 throw std::runtime_error("Bad magic");
47 uint32_t filesystem::allocate_cluster()
49 for(unsigned i
= 0; i
< supercluster_count
; i
++) {
50 supercluster
& c
= superclusters
[i
];
52 for(unsigned j
= 0; j
< CLUSTERS_PER_SUPER
; j
++)
56 //Write zeroes over the cluster.
57 uint32_t cluster
= i
* CLUSTERS_PER_SUPER
+ j
;
58 char buffer
[CLUSTER_SIZE
];
59 memset(buffer
, 0, CLUSTER_SIZE
);
60 backing
.seekp(cluster
* CLUSTER_SIZE
, std::ios_base::beg
);
61 backing
.write(buffer
, CLUSTER_SIZE
);
63 throw std::runtime_error("Can't zero out the new cluster");
67 //Create a new supercluster.
68 supercluster
& c
= superclusters
[supercluster_count
];
69 c
.free_clusters
= 65535;
70 c
.clusters
[0] = 0xFFFFFFFFU
; //Reserved for cluster table.
71 for(unsigned i
= 1; i
< CLUSTERS_PER_SUPER
; i
++)
72 c
.clusters
[i
] = 0; //Free.
73 if(!supercluster_count
)
74 c
.clusters
[1] = 0xFFFFFFFFU
; //Reserved for superblock.
75 unsigned j
= supercluster_count
? 1 : 2;
76 if(!supercluster_count
)
77 c
.free_clusters
--; //The superblock.
78 c
.clusters
[j
] = 1; //End of chain.
79 c
.save(backing
, supercluster_count
);
80 char blankbuf
[2 * CLUSTER_SIZE
];
81 memset(blankbuf
, 0, 2 * CLUSTER_SIZE
);
83 backing
.seekp(supercluster_count
* SUPERCLUSTER_SIZE
+ CLUSTER_SIZE
, std::ios_base::beg
);
84 backing
.write(blankbuf
, supercluster_count
? CLUSTER_SIZE
: 2 * CLUSTER_SIZE
);
86 throw std::runtime_error("Can't write new supercluster");
87 return (supercluster_count
++) * CLUSTERS_PER_SUPER
+ j
;
90 void filesystem::link_cluster(uint32_t cluster
, uint32_t linkto
)
92 if(cluster
/ CLUSTERS_PER_SUPER
>= supercluster_count
)
93 throw std::runtime_error("Bad cluster to link from");
94 if(linkto
/ CLUSTERS_PER_SUPER
>= supercluster_count
)
95 throw std::runtime_error("Bad cluster to link to");
96 if(superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
% CLUSTERS_PER_SUPER
] != 1)
97 throw std::runtime_error("Only end of chain clusters can be linked");
98 superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
% CLUSTERS_PER_SUPER
] = linkto
;
99 superclusters
[cluster
/ CLUSTERS_PER_SUPER
].save(backing
, cluster
/ CLUSTERS_PER_SUPER
);
102 void filesystem::free_cluster_chain(uint32_t cluster
)
105 throw std::runtime_error("Cluster 2 can't be freed");
106 if(cluster
/ CLUSTERS_PER_SUPER
>= supercluster_count
)
107 throw std::runtime_error("Bad cluster to free");
108 uint32_t oldnext
= superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
% CLUSTERS_PER_SUPER
];
110 throw std::runtime_error("Attempted to free free cluster");
111 if(oldnext
== 0xFFFFFFFFU
)
112 throw std::runtime_error("Attempted to free system cluster");
113 superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
% CLUSTERS_PER_SUPER
] = 0;
114 //If there is no next block, or the next block is in different supercluster, save the cluster table.
115 if(oldnext
== 1 || oldnext
/ CLUSTERS_PER_SUPER
!= cluster
/ CLUSTERS_PER_SUPER
)
116 superclusters
[cluster
/ CLUSTERS_PER_SUPER
].save(backing
, cluster
/ CLUSTERS_PER_SUPER
);
118 free_cluster_chain(oldnext
);
121 size_t filesystem::skip_data(uint32_t& cluster
, uint32_t& ptr
, uint32_t length
)
125 if(cluster
/ CLUSTERS_PER_SUPER
>= supercluster_count
|| (cluster
% CLUSTERS_PER_SUPER
) == 0)
126 throw std::runtime_error("Bad cluster to read");
127 if(!superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
% CLUSTERS_PER_SUPER
])
128 throw std::runtime_error("Bad cluster to read");
129 //Read to end of cluster.
130 size_t maxread
= min(length
, max(static_cast<uint32_t>(CLUSTER_SIZE
), ptr
) - ptr
);
136 if(ptr
>= CLUSTER_SIZE
) {
137 uint32_t n
= superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
%
140 throw std::runtime_error("Bad next cluster when reading");
141 else if(n
== 1 || n
== 0xFFFFFFFFU
) {
154 size_t filesystem::read_data(uint32_t& cluster
, uint32_t& ptr
, void* data
, uint32_t length
)
156 char* _data
= reinterpret_cast<char*>(data
);
159 if(cluster
/ CLUSTERS_PER_SUPER
>= supercluster_count
|| (cluster
% CLUSTERS_PER_SUPER
) == 0)
160 throw std::runtime_error("Bad cluster to read");
161 if(!superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
% CLUSTERS_PER_SUPER
])
162 throw std::runtime_error("Bad cluster to read");
163 //Read to end of cluster.
164 size_t maxread
= min(length
, max(static_cast<uint32_t>(CLUSTER_SIZE
), ptr
) - ptr
);
167 backing
.seekg(cluster
* CLUSTER_SIZE
+ ptr
, std::ios_base::beg
);
168 backing
.read(_data
, maxread
);
170 throw std::runtime_error("Can't read data");
176 if(ptr
>= CLUSTER_SIZE
) {
177 uint32_t n
= superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
%
180 throw std::runtime_error("Bad next cluster when reading");
181 else if(n
== 1 || n
== 0xFFFFFFFFU
) {
194 void filesystem::write_data(uint32_t& cluster
, uint32_t& ptr
, const void* data
, uint32_t length
,
195 uint32_t& real_cluster
, uint32_t& real_ptr
)
197 const char* _data
= reinterpret_cast<const char*>(data
);
199 bool assigned
= false;
201 if(cluster
/ CLUSTERS_PER_SUPER
>= supercluster_count
|| (cluster
% CLUSTERS_PER_SUPER
) == 0)
202 throw std::runtime_error("Bad cluster to write");
203 if(!superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
% CLUSTERS_PER_SUPER
])
204 throw std::runtime_error("Bad cluster to write");
205 //Write to end of cluster.
206 size_t maxwrite
= min(length
, max(static_cast<uint32_t>(CLUSTER_SIZE
), ptr
) - ptr
);
208 char buffer
[CLUSTER_SIZE
];
209 memset(buffer
, 0, CLUSTER_SIZE
);
211 real_cluster
= cluster
;
215 backing
.seekp(0, std::ios_base::end
);
216 uint64_t backing_size
= backing
.tellp();
217 if(backing_size
> cluster
* CLUSTER_SIZE
) {
218 backing
.seekg(cluster
* CLUSTER_SIZE
, std::ios_base::beg
);
219 backing
.read(buffer
, CLUSTER_SIZE
);
221 memcpy(buffer
+ ptr
, _data
, maxwrite
);
222 backing
.seekp(cluster
* CLUSTER_SIZE
, std::ios_base::beg
);
223 backing
.write(buffer
, CLUSTER_SIZE
);
225 throw std::runtime_error("Can't write data");
231 if(ptr
>= CLUSTER_SIZE
) {
232 uint32_t n
= superclusters
[cluster
/ CLUSTERS_PER_SUPER
].clusters
[cluster
%
235 throw std::runtime_error("Bad next cluster when writing");
236 else if(n
== 0xFFFFFFFFU
) {
238 throw std::runtime_error("Bad next cluster when writing");
246 n
= allocate_cluster();
247 link_cluster(cluster
, n
);
258 void filesystem::supercluster::load(std::fstream
& s
, uint32_t index
)
260 uint64_t offset
= SUPERCLUSTER_SIZE
* index
;
261 char buffer
[CLUSTER_SIZE
];
263 s
.seekg(offset
, std::ios_base::beg
);
264 s
.read(buffer
, CLUSTER_SIZE
);
266 throw std::runtime_error("Can't read cluster table");
268 for(unsigned i
= 0; i
< CLUSTERS_PER_SUPER
; i
++) {
269 if(!(clusters
[i
] = serialization::u32b(buffer
+ 4 * i
)))
274 void filesystem::supercluster::save(std::fstream
& s
, uint32_t index
)
276 uint64_t offset
= SUPERCLUSTER_SIZE
* index
;
277 char buffer
[CLUSTER_SIZE
];
278 for(unsigned i
= 0; i
< CLUSTERS_PER_SUPER
; i
++)
279 serialization::u32b(buffer
+ 4 * i
, clusters
[i
]);
281 s
.seekp(offset
, std::ios_base::beg
);
282 s
.write(buffer
, CLUSTER_SIZE
);
284 throw std::runtime_error("Can't write cluster table");
287 filesystem::ref
& filesystem::ref::operator=(const filesystem::ref
& r
)
291 //This is tricky, due to having to lock two objects.
292 threads::lock
* mtodelete
= NULL
;
294 //We just have to grab a ref and copy.
295 threads::alock
m(*r
.mlock
);
302 threads::alock_multiple
ms({r
.mlock
, mlock
});