xref: /haiku/src/servers/registrar/mime/MimeUpdateThread.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
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 \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 */
51 MimeUpdateThread::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 */
72 MimeUpdateThread::~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 */
82 status_t
83 MimeUpdateThread::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 */
96 status_t
97 MimeUpdateThread::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 */
149 bool
150 MimeUpdateThread::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 */
186 status_t
187 MimeUpdateThread::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