xref: /haiku/src/servers/registrar/mime/MimeUpdateThread.cpp (revision d0ac609964842f8cdb6d54b3c539c6c15293e172)
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 
31 namespace BPrivate {
32 namespace Storage {
33 namespace 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 */
52 MimeUpdateThread::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 */
73 MimeUpdateThread::~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 */
83 status_t
84 MimeUpdateThread::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 */
97 status_t
98 MimeUpdateThread::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 */
151 bool
152 MimeUpdateThread::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 */
187 status_t
188 MimeUpdateThread::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