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