xref: /haiku/src/apps/diskprobe/DataEditor.cpp (revision 1b89aa98ffe3057ab801dab67b9f5de6d1f933a5)
1 /*
2 ** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 ** Distributed under the terms of the OpenBeOS License.
4 */
5 
6 
7 #include "DataEditor.h"
8 
9 #include <Autolock.h>
10 #include <NodeMonitor.h>
11 #include <Drivers.h>
12 
13 #include <string.h>
14 #include <unistd.h>
15 
16 
17 class DataChange {
18 	public:
19 		virtual ~DataChange();
20 
21 		virtual void Apply(off_t offset, uint8 *buffer, size_t size) = 0;
22 		virtual void Revert(off_t offset, uint8 *buffer, size_t size) = 0;
23 };
24 
25 class ReplaceChange : public DataChange {
26 	public:
27 		ReplaceChange(off_t offset, const uint8 *data, size_t size);
28 		~ReplaceChange();
29 
30 		virtual void Apply(off_t offset, uint8 *buffer, size_t size);
31 		virtual void Revert(off_t offset, uint8 *buffer, size_t size);
32 
33 	private:
34 		void Normalize(off_t bufferOffset, size_t bufferSize,
35 			off_t &offset, size_t &dataOffset, size_t &size);
36 
37 		uint8 	*fNewData;
38 		uint8 	*fOldData;
39 		size_t	fSize;
40 		off_t	fOffset;
41 };
42 
43 
44 DataChange::~DataChange()
45 {
46 }
47 
48 
49 //	#pragma mark -
50 
51 
52 ReplaceChange::ReplaceChange(off_t offset, const uint8 *data, size_t size)
53 {
54 	fOffset = offset;
55 
56 	fNewData = (uint8 *)malloc(size);
57 	fOldData = (uint8 *)malloc(size);
58 	if (fNewData != NULL && fOldData != NULL) {
59 		memcpy(fNewData, data, size);
60 		fSize = size;
61 	} else
62 		fSize = 0;
63 }
64 
65 
66 ReplaceChange::~ReplaceChange()
67 {
68 	free(fNewData);
69 	free(fOldData);
70 }
71 
72 
73 /** Normalizes the supplied offset & size pair to be limited by
74  *	the buffer offset and size.
75  *	All parameters must have been initialized before calling this
76  *	method.
77  */
78 
79 void
80 ReplaceChange::Normalize(off_t bufferOffset, size_t bufferSize, off_t &offset,
81 	size_t &dataOffset, size_t &size)
82 {
83 	if (fOffset < bufferOffset) {
84 		offset = bufferOffset;
85 		dataOffset = bufferOffset - fOffset;
86 		size -= dataOffset;
87 	}
88 
89 	if (offset + size > bufferOffset + bufferSize)
90 		size = offset - bufferOffset + offset;
91 }
92 
93 
94 void
95 ReplaceChange::Apply(off_t bufferOffset, uint8 *buffer, size_t bufferSize)
96 {
97 	// is it in our range?
98 	if (fOffset - bufferOffset > bufferSize || fOffset + fSize < bufferOffset)
99 		return;
100 
101 	// don't change anything outside the supplied buffer
102 	off_t offset = fOffset;
103 	size_t dataOffset = 0;
104 	size_t size = fSize;
105 	Normalize(bufferOffset, bufferSize, offset, dataOffset, size);
106 	if (size == 0)
107 		return;
108 
109 	// now we can safely exchange the buffer!
110 	memcpy(fOldData + dataOffset, buffer + offset - bufferOffset, size);
111 	memcpy(buffer + offset - bufferOffset, fNewData + dataOffset, size);
112 }
113 
114 
115 void
116 ReplaceChange::Revert(off_t bufferOffset, uint8 *buffer, size_t bufferSize)
117 {
118 	// is it in our range?
119 	if (fOffset - bufferOffset > bufferSize || fOffset + fSize < bufferOffset)
120 		return;
121 
122 	// don't change anything outside the supplied buffer
123 	off_t offset = fOffset;
124 	size_t dataOffset = 0;
125 	size_t size = fSize;
126 	Normalize(bufferOffset, bufferSize, offset, dataOffset, size);
127 	if (size == 0)
128 		return;
129 
130 	// now we can safely revert the buffer!
131 	memcpy(buffer + offset - bufferOffset, fOldData + dataOffset, size);
132 }
133 
134 
135 //	#pragma mark -
136 
137 
138 DataEditor::DataEditor()
139 	: BLocker("data view")
140 {
141 }
142 
143 
144 DataEditor::DataEditor(entry_ref &ref, const char *attribute)
145 	: BLocker("data view")
146 {
147 	SetTo(ref, attribute);
148 }
149 
150 
151 DataEditor::DataEditor(BEntry &entry, const char *attribute)
152 	: BLocker("data view")
153 {
154 	SetTo(entry, attribute);
155 }
156 
157 
158 DataEditor::DataEditor(const DataEditor &editor)
159 	: BLocker("data view")
160 {
161 }
162 
163 
164 DataEditor::~DataEditor()
165 {
166 }
167 
168 
169 status_t
170 DataEditor::SetTo(const char *path, const char *attribute)
171 {
172 	BEntry entry(path);
173 	return SetTo(entry, attribute);
174 }
175 
176 
177 status_t
178 DataEditor::SetTo(entry_ref &ref, const char *attribute)
179 {
180 	BEntry entry(&ref);
181 	return SetTo(entry, attribute);
182 }
183 
184 
185 status_t
186 DataEditor::SetTo(BEntry &entry, const char *attribute)
187 {
188 	status_t status = fFile.SetTo(&entry, B_READ_WRITE);
189 	if (status < B_OK) {
190 		// try to open read only
191 		status = fFile.SetTo(&entry, B_READ_ONLY);
192 		if (status < B_OK)
193 			return status;
194 
195 		fIsReadOnly = true;
196 	}
197 
198 	struct stat stat;
199 	stat.st_mode = 0;
200 
201 	fFile.GetStat(&stat);
202 	fIsDevice = (stat.st_mode & (S_IFBLK | S_IFCHR)) != 0;
203 
204 	fBlockSize = 512;
205 
206 	if (fIsDevice) {
207 		// ToDo: is there any other possibility to issue a ioctl() from a BFile?
208 		device_geometry geometry;
209 		int device = fFile.Dup();
210 		if (device < 0 || ioctl(device, B_GET_GEOMETRY, &geometry) < 0) {
211 			if (device >= 0)
212 				close(device);
213 			fFile.Unset();
214 			return B_ERROR;
215 		}
216 		close(device);
217 
218 		fSize = 1LL * geometry.head_count * geometry.cylinder_count
219 			* geometry.sectors_per_track * geometry.bytes_per_sector;
220 		fBlockSize = geometry.bytes_per_sector;
221 	} else if (IsAttribute()) {
222 		// ToDo: add support for attributes!
223 		fSize = 0;
224 	} else {
225 		status = fFile.GetSize(&fSize);
226 		if (status < B_OK) {
227 			fFile.Unset();
228 			return status;
229 		}
230 	}
231 
232 	if (attribute != NULL)
233 		fAttribute = strdup(attribute);
234 	else
235 		fAttribute = NULL;
236 
237 	fView = NULL;
238 	fRealViewOffset = 0;
239 	fViewOffset = 0;
240 	fRealViewSize = fViewSize = fBlockSize;
241 	fNeedsUpdate = true;
242 
243 	return B_OK;
244 }
245 
246 
247 status_t
248 DataEditor::SetToAttribute(const char *attribute)
249 {
250 	status_t status = InitCheck();
251 	if (status < B_OK)
252 		return status;
253 
254 	fAttribute = attribute;
255 
256 	// ToDo: attributes are not yet supported
257 	return B_ERROR;
258 }
259 
260 
261 status_t
262 DataEditor::InitCheck()
263 {
264 	if (fAttribute != NULL)
265 		// ToDo: for now!
266 		return B_ERROR;
267 
268 	return fFile.InitCheck();
269 }
270 
271 
272 void
273 DataEditor::AddChange(DataChange *change)
274 {
275 	if (change == NULL)
276 		return;
277 
278 	RemoveRedos();
279 
280 	fChanges.AddItem(change);
281 	fLastChange = change;
282 
283 	fLastChange->Apply(fRealViewOffset, fView, fRealViewSize);
284 }
285 
286 
287 status_t
288 DataEditor::Replace(off_t offset, const uint8 *data, size_t length)
289 {
290 	if (!IsLocked())
291 		debugger("DataEditor: view not locked");
292 
293 	if (fNeedsUpdate) {
294 		status_t status = Update();
295 		if (status < B_OK)
296 			return status;
297 	}
298 
299 	ReplaceChange *change = new ReplaceChange(offset, data, length);
300 	AddChange(change);
301 
302 	return B_OK;
303 }
304 
305 
306 status_t
307 DataEditor::Remove(off_t offset, off_t length)
308 {
309 	if (!IsLocked())
310 		debugger("DataEditor: view not locked");
311 
312 	// not yet implemented
313 
314 	return B_ERROR;
315 }
316 
317 
318 status_t
319 DataEditor::Insert(off_t offset, const uint8 *text, size_t length)
320 {
321 	if (!IsLocked())
322 		debugger("DataEditor: view not locked");
323 
324 	// not yet implemented
325 
326 	return B_ERROR;
327 }
328 
329 
330 void
331 DataEditor::ApplyChanges()
332 {
333 	if (fLastChange == NULL)
334 		return;
335 
336 	int32 count = fChanges.IndexOf(fLastChange) + 1;
337 
338 	for (int32 i = 0; i < count; i++) {
339 		DataChange *change = fChanges.ItemAt(i);
340 		change->Apply(fRealViewOffset, fView, fRealViewSize);
341 	}
342 }
343 
344 
345 /** This method will be called by DataEditor::AddChange()
346  *	immediately before a change is applied.
347  *	It removes all pending redo nodes from the list that would
348  *	come after the current change.
349  */
350 
351 void
352 DataEditor::RemoveRedos()
353 {
354 	if (fLastChange == NULL)
355 		return;
356 
357 	int32 start = fChanges.IndexOf(fLastChange) + 1;
358 
359 	for (int32 i = fChanges.CountItems(); i-- > start; ) {
360 		DataChange *change = fChanges.RemoveItemAt(i);
361 		delete change;
362 	}
363 }
364 
365 
366 status_t
367 DataEditor::Undo()
368 {
369 	BAutolock locker(this);
370 
371 	if (!CanUndo())
372 		return B_ERROR;
373 
374 	int32 index = fChanges.IndexOf(fLastChange);
375 	fLastChange->Revert(fRealViewOffset, fView, fRealViewSize);
376 
377 	if (index > 0)
378 		fLastChange = fChanges.ItemAt(index - 1);
379 	else
380 		fLastChange = NULL;
381 
382 	return B_OK;
383 }
384 
385 
386 status_t
387 DataEditor::Redo()
388 {
389 	BAutolock locker(this);
390 
391 	if (!CanRedo())
392 		return B_ERROR;
393 
394 	int32 index = fChanges.IndexOf(fLastChange);
395 	fLastChange = fChanges.ItemAt(index + 1);
396 
397 	fLastChange->Apply(fRealViewOffset, fView, fRealViewSize);
398 
399 	return B_OK;
400 }
401 
402 
403 bool
404 DataEditor::CanUndo() const
405 {
406 	return fLastChange != NULL;
407 }
408 
409 
410 bool
411 DataEditor::CanRedo() const
412 {
413 	return fChanges.IndexOf(fLastChange) < fChanges.CountItems() - 1;
414 }
415 
416 
417 status_t
418 DataEditor::SetFileSize(off_t size)
419 {
420 	fSize = size;
421 	return B_OK;
422 }
423 
424 
425 status_t
426 DataEditor::SetViewOffset(off_t offset)
427 {
428 	if (fView == NULL) {
429 		status_t status = SetViewSize(fViewSize);
430 		if (status < B_OK)
431 			return status;
432 	}
433 
434 	fRealViewOffset = (offset / fBlockSize) * fBlockSize;
435 	fViewOffset = offset;
436 	fNeedsUpdate = true;
437 
438 	return B_OK;
439 }
440 
441 
442 status_t
443 DataEditor::SetViewSize(size_t size)
444 {
445 	size_t realSize = (size + fBlockSize - 1) & ~(fBlockSize - 1);
446 		// round to the next multiple of block size
447 
448 	if (realSize == fRealViewSize && fView != NULL) {
449 		fViewSize = size;
450 		return B_OK;
451 	}
452 
453 	uint8 *view = (uint8 *)realloc(fView, realSize);
454 	if (view == NULL)
455 		return B_NO_MEMORY;
456 
457 	fView = view;
458 	fRealViewSize = realSize;
459 	fViewSize = size;
460 	fNeedsUpdate = true;
461 
462 	return B_OK;
463 }
464 
465 
466 void
467 DataEditor::SetBlockSize(size_t size)
468 {
469 	fBlockSize = size;
470 }
471 
472 
473 status_t
474 DataEditor::Update()
475 {
476 	ssize_t bytesRead = fFile.ReadAt(fRealViewOffset, fView, fRealViewSize);
477 	if (bytesRead < B_OK)
478 		return bytesRead;
479 
480 	ApplyChanges();
481 	fNeedsUpdate = false;
482 
483 	return B_OK;
484 }
485 
486 
487 status_t
488 DataEditor::GetViewBuffer(const uint8 **_buffer)
489 {
490 	if (!IsLocked())
491 		debugger("DataEditor: view not locked");
492 
493 	status_t status = B_OK;
494 
495 	if (fView == NULL)
496 		status = SetViewOffset(fViewOffset);
497 
498 	if (status == B_OK && fNeedsUpdate)
499 		status = Update();
500 
501 	if (status < B_OK)
502 		return status;
503 
504 	*_buffer = fView + fViewOffset - fRealViewOffset;
505 	return B_OK;
506 }
507 
508 
509 void
510 DataEditor::SendNotices(uint32 what, BMessage *message)
511 {
512 	if (fObservers.CountItems() == 0)
513 		return;
514 
515 	BMessage *notice;
516 	if (message) {
517 		notice = new BMessage(*message);
518 		notice->what = B_OBSERVER_NOTICE_CHANGE;
519 		notice->AddInt32(B_OBSERVE_ORIGINAL_WHAT, message->what);
520 	} else
521 		notice = new BMessage(B_OBSERVER_NOTICE_CHANGE);
522 
523 	notice->AddInt32(B_OBSERVE_WHAT_CHANGE, what);
524 
525 	for (int32 i = fObservers.CountItems(); i-- > 0;) {
526 		BMessenger *messenger = fObservers.ItemAt(i);
527 		messenger->SendMessage(notice);
528 	}
529 
530 	delete notice;
531 }
532 
533 
534 status_t
535 DataEditor::StartWatching(BMessenger target)
536 {
537 	BAutolock locker(this);
538 
539 	node_ref node;
540 	status_t status = fFile.GetNodeRef(&node);
541 	if (status < B_OK)
542 		return status;
543 
544 	fObservers.AddItem(new BMessenger(target));
545 
546 	return watch_node(&node, B_WATCH_STAT | B_WATCH_ATTR, target);
547 }
548 
549 
550 status_t
551 DataEditor::StartWatching(BHandler *handler, BLooper *looper)
552 {
553 	return StartWatching(BMessenger(handler, looper));
554 }
555 
556 
557 void
558 DataEditor::StopWatching(BMessenger target)
559 {
560 	BAutolock locker(this);
561 
562 	for (int32 i = fObservers.CountItems(); i-- > 0;) {
563 		BMessenger *messenger = fObservers.ItemAt(i);
564 		if (*messenger == target) {
565 			fObservers.RemoveItemAt(i);
566 			delete messenger;
567 			break;
568 		}
569 	}
570 
571 	stop_watching(target);
572 }
573 
574 
575 void
576 DataEditor::StopWatching(BHandler *handler, BLooper *looper)
577 {
578 	StopWatching(BMessenger(handler, looper));
579 }
580 
581