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