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