1/*
2 * Copyright 2002-2006, Haiku Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Tyler Dauwalder
7 *		Ingo Weinhold, bonefish@users.sf.net
8 */
9
10/*!
11	\file MimeUpdateThread.cpp
12	MimeUpdateThread implementation
13*/
14
15#include "MimeUpdateThread.h"
16
17#include <stdio.h>
18
19#include <Directory.h>
20#include <Message.h>
21#include <Path.h>
22#include <RegistrarDefs.h>
23#include <Volume.h>
24
25#include <storage_support.h>
26
27//#define DBG(x) x
28#define DBG(x)
29#define OUT printf
30
31namespace BPrivate {
32namespace Storage {
33namespace Mime {
34
35/*!	\class MimeUpdateThread
36	\brief RegistrarThread class implementing the common functionality of
37	update_mime_info() and create_app_meta_mime()
38*/
39
40
41/*! \brief Creates a new MimeUpdateThread object.
42
43	If \a replyee is non-NULL and construction succeeds, the MimeThreadObject
44	assumes resposibility for its deletion.
45
46	Also, if \c non-NULL, \a replyee is expected to be a
47	\c B_REG_MIME_UPDATE_MIME_INFO or a \c B_REG_MIME_CREATE_APP_META_MIME
48	message with a \c true \c "synchronous" field detached from the registrar's
49	mime manager looper (though this is not verified).
50	The message will be	replied to at the end of the thread's execution.
51*/
52MimeUpdateThread::MimeUpdateThread(const char *name, int32 priority,
53		Database *database, BMessenger managerMessenger, const entry_ref *root,
54		bool recursive, int32 force, BMessage *replyee)
55	:
56	RegistrarThread(name, priority, managerMessenger),
57	fDatabase(database),
58	fRoot(root ? *root : entry_ref()),
59	fRecursive(recursive),
60	fForce(force),
61	fReplyee(replyee),
62	fStatus(root ? B_OK : B_BAD_VALUE)
63{
64}
65
66
67/*!	\brief Destroys the MimeUpdateThread object.
68
69	If the object was properly initialized (i.e. InitCheck() returns \c B_OK)
70	and the replyee message passed to the constructor was \c non-NULL, the
71	replyee message is deleted.
72*/
73MimeUpdateThread::~MimeUpdateThread()
74{
75	// delete our acquired BMessage
76	if (InitCheck() == B_OK)
77		delete fReplyee;
78}
79
80
81/*! \brief Returns the initialization status of the object
82*/
83status_t
84MimeUpdateThread::InitCheck()
85{
86	status_t err = RegistrarThread::InitCheck();
87	if (!err)
88		err = fStatus;
89	return err;
90}
91
92
93/*! \brief Implements the common functionality of update_mime_info() and
94	create_app_meta_mime(), namely iterating through the filesystem and
95	updating entries.
96*/
97status_t
98MimeUpdateThread::ThreadFunction()
99{
100	status_t err = InitCheck();
101
102	// The registrar is using this, too, so we better make sure we
103	// don't run into troubles
104	try {
105		// Do the updates
106		if (!err)
107			err = UpdateEntry(&fRoot);
108	} catch (...) {
109		err = B_ERROR;
110	}
111
112	// Send a reply if we have a message to reply to
113	if (fReplyee) {
114		BMessage reply(B_REG_RESULT);
115		status_t error = reply.AddInt32("result", err);
116		err = error;
117		if (!err)
118			err = fReplyee->SendReply(&reply);
119	}
120
121	// Flag ourselves as finished
122	fIsFinished = true;
123	// Notify the thread manager to make a cleanup run
124	if (!err) {
125		BMessage msg(B_REG_MIME_UPDATE_THREAD_FINISHED);
126		status_t error = fManagerMessenger.SendMessage(&msg, (BHandler*)NULL,
127			500000);
128		if (error)
129			OUT("WARNING: ThreadManager::ThreadEntryFunction(): Termination"
130				" notification failed with error 0x%" B_PRIx32 "\n", error);
131	}
132	DBG(OUT("(id: %ld) exiting mime update thread with result 0x%" B_PRIx32
133		"\n", find_thread(NULL), err));
134	return err;
135}
136
137
138/*! \brief Returns true if the given device supports attributes, false
139	if not (or if an error occurs while determining).
140
141	Device numbers and their corresponding support info are cached in
142	a std::list to save unnecessarily \c statvfs()ing devices that have
143	already been statvfs()ed (which might otherwise happen quite often
144	for a device that did in fact support attributes).
145
146	\return
147	- \c true: The device supports attributes
148	- \c false: The device does not support attributes, or there was an
149	            error while determining
150*/
151bool
152MimeUpdateThread::DeviceSupportsAttributes(dev_t device)
153{
154	// See if an entry for this device already exists
155	std::list< std::pair<dev_t,bool> >::iterator i;
156	for (i = fAttributeSupportList.begin();
157			i != fAttributeSupportList.end(); i++)
158	{
159		if (i->first == device)
160			return i->second;
161	}
162
163	bool result = false;
164
165	// If we get here, no such device is yet in our list,
166	// so we attempt to remedy the situation
167	BVolume volume;
168	status_t err = volume.SetTo(device);
169	if (!err) {
170		result = volume.KnowsAttr();
171		// devices supporting attributes are likely to be queried
172		// again, devices not supporting attributes are not
173		std::pair<dev_t,bool> p(device, result);
174		if (result)
175			fAttributeSupportList.push_front(p);
176		else
177			fAttributeSupportList.push_back(p);
178	}
179
180	return result;
181}
182
183// UpdateEntry
184/*! \brief Updates the given entry and then recursively updates all the entry's
185	child entries if the entry is a directory and \c fRecursive is true.
186*/
187status_t
188MimeUpdateThread::UpdateEntry(const entry_ref *ref)
189{
190	status_t err = ref ? B_OK : B_BAD_VALUE;
191	bool entryIsDir = false;
192
193	// Look to see if we're being terminated
194	if (!err && fShouldExit)
195		err = B_CANCELED;
196
197	// Before we update, make sure this entry lives on a device that supports
198	// attributes. If not, we skip it and any of its children for
199	// updates (we don't signal an error, however).
200
201//BPath path(ref);
202//printf("Updating '%s' (%s)... \n", path.Path(),
203//	(DeviceSupportsAttributes(ref->device) ? "yes" : "no"));
204
205	if (!err && (device_is_root_device(ref->device)
206				|| DeviceSupportsAttributes(ref->device))) {
207		// Update this entry
208		if (!err) {
209			// R5 appears to ignore whether or not the update succeeds.
210			DoMimeUpdate(ref, &entryIsDir);
211		}
212
213		// If we're recursing and this is a directory, update
214		// each of the directory's children as well
215		if (!err && fRecursive && entryIsDir) {
216			BDirectory dir;
217			err = dir.SetTo(ref);
218			if (!err) {
219				entry_ref childRef;
220				while (!err) {
221					err = dir.GetNextRef(&childRef);
222					if (err) {
223						// If we've come to the end of the directory listing,
224						// it's not an error.
225						if (err == B_ENTRY_NOT_FOUND)
226							err = B_OK;
227						break;
228					} else
229						err = UpdateEntry(&childRef);
230				}
231			}
232		}
233	}
234	return err;
235}
236
237}	// namespace Mime
238}	// namespace Storage
239}	// namespace BPrivate
240