internal override void ImportThreadMain(string sourceDbPath, VolumeDatabase targetDb, string dbDataPath, BufferedVolumeItemWriter writer) { this.counters = new long[3]; //idCounter = 2; // id 1 is the root item //totalMedia = 0; this.path = new Stack <string>(); this.mimePathPrefix = GetNonExistingPath() + "/"; this.targetDb = targetDb; this.writer = writer; using (GZipStream s = new GZipStream(File.OpenRead(sourceDbPath), CompressionMode.Decompress)) { XmlReaderSettings settings = new XmlReaderSettings() { DtdProcessing = DtdProcessing.Ignore, ValidationType = ValidationType.None, CheckCharacters = false }; XmlReader reader = XmlTextReader.Create(s, settings); XmlDocument xml = new XmlDocument(); xml.Load(reader); string dummy1 = null; MetadataStore dummy2 = MetadataStore.Empty; RecursiveDump(xml.DocumentElement, 0L, 0L, ref dummy1, ref dummy1, ref dummy2); } }
private static void ImportFile(IDataReader reader, long volumeID, long minFileID, string rootPath, MetadataStore metaData, VolumeDatabase db, BufferedVolumeItemWriter writer, long[] counters) { FileSystemVolumeItem item; if ((string)reader["type"] == "directory") { item = new DirectoryVolumeItem(db); counters[TOTAL_DIRS]++; } else { item = new FileVolumeItem(db); long size = (long)reader["size"]; ((FileVolumeItem)item).SetFileVolumeItemFields(size, null); counters[TOTAL_FILES]++; counters[TOTAL_SIZE] += size; } string path = (string)reader["path"]; Debug.Assert(path.StartsWith("file://"), "path starts with 'file://'"); string name = (string)reader["name"]; string location = DecoderUtility.UrlDecode(path); location = location.Substring(rootPath.Length); location = location.Substring(0, location.Length - name.Length - 1); if (location.Length == 0) { location = "/"; } long itemID = 2 + (long)reader["id"] - minFileID; // id 1 is the root item long parentID = Math.Max(1, 2 + (long)reader["idparent"] - minFileID); item.SetFileSystemVolumeItemFields(location, DateTime.MinValue, VolumeDatabase.ID_NONE); item.SetVolumeItemFields(volumeID, itemID, parentID, name, Util.ReplaceDBNull <string>(reader["mime"], null), metaData, Util.ReplaceDBNull <string>(reader["comment"], null), null); writer.Write(item); }
private long InsertFile(string rootPath, FileInfo file, BufferedVolumeItemWriter writer, long parentID, string mimeType, MetadataStore metaData, string hash) { /* if scanner has no db associated, just update the counters * and return */ if (!this.HasDB) { Interlocked.Increment(ref VolumeInfo.files); Interlocked.Add(ref VolumeInfo.size, file.Length); return(VolumeDatabase.ID_NONE); } DateTime lastWriteTime = GetLastWriteTime(file); FileVolumeItem item = GetNewVolumeItem <FileVolumeItem>(parentID, file.Name, mimeType, metaData, VolumeItemType.FileVolumeItem); item.SetFileSystemVolumeItemFields(GetLocation(file.FullName, rootPath), lastWriteTime, VolumeDatabase.ID_NONE); item.SetFileVolumeItemFields(file.Length, hash); //item.Name = file.Name; // set the items name (defined on VolumeItem baseclass) writer.Write(item); Interlocked.Increment(ref VolumeInfo.files); Interlocked.Add(ref VolumeInfo.size, file.Length); if (!Options.DiscardSymLinks) { symLinkHelper.AddFile(file.FullName, item.ItemID); } return(item.ItemID); }
public WaitHandle RunAsync() { if (isRunning) { throw new InvalidOperationException("Scanner is already running"); } if (scanSucceeded) { throw new InvalidOperationException("Scanning has been completed successfully. Create a new scanner to scan another volume"); } try { /* must be set (as soon as possible) in a function that is _not_ called asynchronously * (i.e. dont call it in ScanningThread()) */ isRunning = true; cancellationRequested = false; asyncOperation = AsyncOperationManager.CreateOperation(null); Reset(); BufferedVolumeItemWriter writer = null; if (this.HasDB) { writer = new BufferedVolumeItemWriter(database, true, Options.BufferSize); } /* invoke the scanning function on a new thread and return a waithandle */ Action <PlatformIO.DriveInfo, TVolume, BufferedVolumeItemWriter> st = ScanningThread; IAsyncResult ar = st.BeginInvoke(drive, volume, writer, null, null); return(ar.AsyncWaitHandle); } catch (Exception) { isRunning = false; if (asyncOperation != null) { asyncOperation.OperationCompleted(); } throw; } }
internal override void ScanningThreadMain(Platform.Common.IO.DriveInfo drive, FileSystemVolume volume, BufferedVolumeItemWriter writer) { try { if (Options.GenerateThumbnails) { paths.volumeDataPath = DbData.CreateVolumeDataPath(paths.dbDataPath, volume.VolumeID); paths.thumbnailPath = DbData.CreateVolumeDataThumbsPath(paths.volumeDataPath); } string rootPath = drive.RootPath; // remove possible ending path seperator except for _system_ root paths rootPath = RemoveEndingSlash(rootPath); // if ((rootPath.Length > 1) && (rootPath[rootPath.Length - 1] == Path.DirectorySeparatorChar)) // rootPath = rootPath.Substring(0, rootPath.Length - 1); // make sure the root path exists // (media may have been removed after scanner construction) if (!Directory.Exists(rootPath)) { throw new DirectoryNotFoundException("Root path does not exist"); } DirectoryInfo dir = new DirectoryInfo(rootPath); RecursiveDump(rootPath, dir, writer, VolumeDatabase.ID_NONE); symLinkHelper.InsertSymLinkItems(writer, volume.VolumeID); volume.SetFileSystemVolumeFields(VolumeInfo.Files, VolumeInfo.Directories, VolumeInfo.Size); } catch (Exception) { // try to cleanup try { if ((paths.volumeDataPath != null) && Directory.Exists(paths.volumeDataPath)) { Directory.Delete(paths.volumeDataPath, true); } } catch (Exception) { /* just shut up */ } // rethrow initial exception throw; } }
internal override void ScanningThreadMain(PlatformIO.DriveInfo drive, AudioCdVolume volume, BufferedVolumeItemWriter writer) { if (Options.ComputeHashs) { SendScannerWarning(S._("Hashcode generation not implemented for audio cds yet.")); volume.IsHashed = false; } AudioCdRootVolumeItem root = GetNewVolumeItem <AudioCdRootVolumeItem>(VolumeDatabase.ID_NONE, "/", null, MetadataStore.Empty, VolumeItemType.AudioCdRootVolumeItem); LocalDisc localdisc = LocalDisc.GetFromDevice(drive.Device); if (localdisc == null) { throw new ApplicationException("Could not read contents of the audio cd"); } TimeSpan[] durations = localdisc.GetTrackDurations(); List <AudioTrackVolumeItem> items = new List <AudioTrackVolumeItem>(); for (int i = 0; i < durations.Length; i++) { AudioTrackVolumeItem item = GetNewVolumeItem <AudioTrackVolumeItem>(root.ItemID, "Track " + (i + 1), MIME_TYPE_AUDIO_TRACK, MetadataStore.Empty, VolumeItemType.AudioTrackVolumeItem); item.SetAudioTrackVolumeItemFields(durations[i]); items.Add(item); VolumeInfo.Tracks++; VolumeInfo.Duration = VolumeInfo.Duration.Add(durations[i]); } // retrieve musicbrainz metadata // (the metadata field of AudioTrackVolumeItems is set // depending on the EnableMusicBrainz flag) if (Options.EnableMusicBrainz) { try { // may throw MusicBrainzNotFoundException Release release = Release.Query(localdisc).PerfectMatch(); CheckForCancellationRequest(); if (release == null) { SendScannerWarning(S._("No MusicBrainz metadata available for this disc.")); } else { var tracks = release.GetTracks(); if (tracks.Count != items.Count) { SendScannerWarning(S._("The trackcount retrieved from MusicBrainz does not match the trackcount of the local disc. Skipped.")); } else { string albumTitle = release.GetTitle(); int releaseYear = GetReleaseYear(release); for (int i = 0; i < tracks.Count; i++) { items[i].Name = tracks[i].GetTitle(); items[i].MetaData = GetMetadata(tracks[i], albumTitle, releaseYear); } volume.Title = albumTitle; // preset category ReleaseType rtype = release.GetReleaseType(); if (rtype == ReleaseType.Album || rtype == ReleaseType.EP || rtype == ReleaseType.Compilation || rtype == ReleaseType.Remix) { volume.Category = PRESELECTED_CATEGORY; } } } } catch (MusicBrainzNotFoundException) { SendScannerWarning(S._("Error connecting to MusicBrainz server.")); } } volume.SetAudioCdVolumeFields(VolumeInfo.Tracks, VolumeInfo.Duration); // write items if (this.HasDB) { writer.Write(root); foreach (AudioTrackVolumeItem item in items) { writer.Write(item); } } }
private void ImportThread(string sourceDbPath, VolumeDatabase targetDb, string dbDataPath, int buferSize) { Exception fatalError = null; bool cancelled = false; try { // must be the first call within the try block targetDb.TransactionBegin(); // locks VolumeDatabase if (!File.Exists(sourceDbPath)) { throw new FileNotFoundException("Source database not found"); } // note: // don't use the writer in a using() block here as dispose() would write // buffered items after an exception has been thrown. BufferedVolumeItemWriter writer = new BufferedVolumeItemWriter(targetDb, true, bufferSize); ImportThreadMain(sourceDbPath, targetDb, dbDataPath, writer); writer.Close(); targetDb.TransactionCommit(); // unlocks VolumeDatabase importSucceeded = true; } catch (Exception ex) { Exception cleanupException = null; try { targetDb.TransactionRollback(); // unlocks VolumeDatabase foreach (string path in volumeDataPaths) { Directory.Delete(path, true); } } catch (Exception e) { cleanupException = e; } if (ex is ImportCancelledException) { cancelled = true; } else { /* save the error that caused the import to stop (import failure) */ fatalError = ex; PostError(ex); Debug.WriteLine("Details for exception in ImportThread():\n" + ex.ToString()); } // in case an error occured while cleaning up, // post the error here, _after_ the initial error that made the import fail. if (cleanupException != null) { PostError(cleanupException); } //#if THROW_EXCEPTIONS_ON_ALL_THREADS // if (!(ex is ScanCancelledException)) // throw; //#endif } finally { isRunning = false; PostCompleted(fatalError, cancelled); } }
abstract void ImportThreadMain(string sourceDbPath, VolumeDatabase targetDb, string dbDataPath, BufferedVolumeItemWriter writer);
internal override void ImportThreadMain(string sourceDbPath, VolumeDatabase targetDb, string dbDataPath, BufferedVolumeItemWriter writer) { string thumbsPath = sourceDbPath + "_thumbs"; string sqlDisks = "SELECT * FROM disks ORDER BY id"; string sqlFiles = "SELECT * FROM files WHERE iddisk = {0} ORDER BY iddisk, id"; using (IDbConnection conn = SqliteDB.Open(sourceDbPath, false)) { long totalFiles = CountFiles(conn); long fileCounter = 0; using (IDbCommand cmdDisks = conn.CreateCommand()) { using (IDbCommand cmdFiles = conn.CreateCommand()) { cmdDisks.CommandText = sqlDisks; using (IDataReader readerDisks = cmdDisks.ExecuteReader()) { while (readerDisks.Read()) { long diskID = (long)readerDisks["id"]; long minFileID = GetMinFileID(conn, diskID); string rootPath = "file://" + (string)readerDisks["root"]; long volumeID = targetDb.GetNextVolumeID(); long[] counters = { 0L, 0L, 0L }; string volDBThumbsPath = CreateThumbsDir(dbDataPath, volumeID); cmdFiles.CommandText = string.Format(sqlFiles, diskID); using (IDataReader readerFiles = cmdFiles.ExecuteReader()) { while (readerFiles.Read()) { long fileID = (long)readerFiles["id"]; ImportFile(readerFiles, volumeID, minFileID, rootPath, ConvertMetaData(conn, fileID), targetDb, writer, counters); ImportThumb(fileID, (2 + fileID - minFileID), thumbsPath, volDBThumbsPath); PostProgressUpdate((++fileCounter * 100.0) / totalFiles); CheckForCancellationRequest(); } } ImportDisk(readerDisks, volumeID, targetDb, counters); } } } } } }
private long InsertDir(string rootPath, DirectoryInfo dir, BufferedVolumeItemWriter writer, long parentID) { /* if scanner has no db associated, just update the counters * and return */ if (!this.HasDB) { // TODO : // increase dircounter for symlink to dirs as well? // nautilus refers to selected symlinks to dirs as dirs too. Interlocked.Increment(ref VolumeInfo.directories); return(VolumeDatabase.ID_NONE); } string location; string name; /* if parentID is ID_NONE, the directory is the volumes root dir * -> location = null, name = "/" (analog System.IO.DirectoryInfo) */ if (parentID == VolumeDatabase.ID_NONE) { location = null; name = PATH_SEPARATOR.ToString(); } else { location = GetLocation(dir.FullName, rootPath); name = dir.Name; } DateTime lastWriteTime = GetLastWriteTime(dir); DirectoryVolumeItem item = GetNewVolumeItem <DirectoryVolumeItem>(parentID, name, MIME_TYPE_DIRECTORY, MetadataStore.Empty, VolumeItemType.DirectoryVolumeItem); item.SetFileSystemVolumeItemFields(location, lastWriteTime, VolumeDatabase.ID_NONE); //item.Name = name; // set the items name (defined on VolumeItem baseclass) // if (isSymlink) { // /* don't dump symlink dirs directly into the database, // * they're required to have a target item assigned. // * target items are resolved in an additional step. // */ // symLinkItems.add(symLinkTarget, item); // } else { writer.Write(item); // } // TODO : // increase dircounter for symlink to dirs as well? // nautilus refers to selected symlinks to dirs as dirs too. Interlocked.Increment(ref VolumeInfo.directories); if (!Options.DiscardSymLinks) { symLinkHelper.AddFile(dir.FullName, item.ItemID); } return(item.ItemID); }
abstract void ScanningThreadMain(PlatformIO.DriveInfo drive, TVolume volume, BufferedVolumeItemWriter writer);
private void ImportThread(string sourceDbPath, VolumeDatabase targetDb, string dbDataPath, int buferSize) { Exception fatalError = null; bool cancelled = false; try { // must be the first call within the try block targetDb.TransactionBegin(); // locks VolumeDatabase if (!File.Exists(sourceDbPath)) throw new FileNotFoundException("Source database not found"); // note: // don't use the writer in a using() block here as dispose() would write // buffered items after an exception has been thrown. BufferedVolumeItemWriter writer = new BufferedVolumeItemWriter(targetDb, true, bufferSize); ImportThreadMain(sourceDbPath, targetDb, dbDataPath, writer); writer.Close(); targetDb.TransactionCommit(); // unlocks VolumeDatabase importSucceeded = true; } catch (Exception ex) { Exception cleanupException = null; try { targetDb.TransactionRollback(); // unlocks VolumeDatabase foreach (string path in volumeDataPaths) Directory.Delete(path, true); } catch (Exception e) { cleanupException = e; } if (ex is ImportCancelledException) { cancelled = true; } else { /* save the error that caused the import to stop (import failure) */ fatalError = ex; PostError(ex); Debug.WriteLine("Details for exception in ImportThread():\n" + ex.ToString()); } // in case an error occured while cleaning up, // post the error here, _after_ the initial error that made the import fail. if (cleanupException != null) { PostError(cleanupException); } //#if THROW_EXCEPTIONS_ON_ALL_THREADS // if (!(ex is ScanCancelledException)) // throw; //#endif } finally { isRunning = false; PostCompleted(fatalError, cancelled); } }
// TODO : make this member internally protected in case this language feature has become real // see http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=33c53cf6-2709-4cc9-a408-6cafee4313ef //protected internal abstract void ImportThreadMain(string sourceDbPath, VolumeDatabase targetDb, string dbDataPath, BufferedVolumeItemWriter writer);
public void InsertSymLinkItems(BufferedVolumeItemWriter writer, long volumeID) { if (symLinkItems.Count == 0) { return; } /* if scanner has no db associated, just update the counters * and return */ if (!scanner.HasDB) { foreach (SymLinkItem sli in symLinkItems) { if (sli.isDir) { Interlocked.Increment(ref scanner.VolumeInfo.directories); } else { Interlocked.Increment(ref scanner.VolumeInfo.files); } // TODO : // increase totalsize by size of symlinks too? (not size of target!) // or are symlinks as big as dirs, those aren't respected as well.. //Interlocked.Add(ref VolumeInfo.size, sli.size); } return; } // make sure all files/dirs have been written to the database // before searching for symlink targets. writer.Flush(); foreach (SymLinkItem sli in symLinkItems) { scanner.CheckForCancellationRequest(); long itemID; if (!files.TryGetValue(sli.fullTargetPath, out itemID)) { /* may throw ScanCancelledException */ scanner.SendScannerWarning(string.Format(S._("Failed to resolve target item for symlink '{0}'."), sli.fullPath)); } else { SearchCriteriaGroup g = new SearchCriteriaGroup(MatchRule.AllMustMatch); g.AddSearchCriteria(new IDSearchCriteria(volumeID, IDSearchField.VolumeID, CompareOperator.Equal)); g.AddSearchCriteria(new IDSearchCriteria(itemID, IDSearchField.ItemID, CompareOperator.Equal)); // query target item. // async BeginItemSearch() won't work here // (active transaction prevents other threads from accessing the database) VolumeItem[] queriedItems = scanner.Database.SearchItem(g); FileSystemVolumeItem targetItem = (FileSystemVolumeItem)queriedItems[0]; FileSystemVolumeItem newItem; if (targetItem is FileVolumeItem) { newItem = scanner.GetNewVolumeItem <FileVolumeItem>(sli.parentID, sli.name, targetItem.MimeType, targetItem.MetaData, VolumeItemType.FileVolumeItem); ((FileVolumeItem)newItem).SetFileVolumeItemFields(((FileVolumeItem)targetItem).Size, ((FileVolumeItem)targetItem).Hash); Interlocked.Increment(ref scanner.VolumeInfo.files); } else // DirectoryVolumeItem { newItem = scanner.GetNewVolumeItem <DirectoryVolumeItem>(sli.parentID, sli.name, targetItem.MimeType, targetItem.MetaData, VolumeItemType.DirectoryVolumeItem); Interlocked.Increment(ref scanner.VolumeInfo.directories); } newItem.SetFileSystemVolumeItemFields(sli.location, targetItem.LastWriteTime, targetItem.ItemID); writer.Write(newItem); // TODO : // increase totalsize by size of symlinks too? (not size of target!) // or are symlinks as big as dirs, those aren't respected as well.. //Interlocked.Add(ref VolumeInfo.size, sli.size); if (Global.EnableDebugging) { Debug.WriteLine("Successfully resolved and saved symlink item: {0}/{1} -> {2}/{3}", (sli.location == PATH_SEPARATOR.ToString() ? "" : sli.location), sli.name, (targetItem.Location == PATH_SEPARATOR.ToString() ? "" : targetItem.Location), (targetItem.Name == PATH_SEPARATOR.ToString() ? "" : targetItem.Name)); } } // end if } // end foreach }
private static void ImportFile(IDataReader reader, long volumeID, long minFileID, string rootPath, MetadataStore metaData, VolumeDatabase db, BufferedVolumeItemWriter writer, long[] counters) { FileSystemVolumeItem item; if ((string)reader["type"] == "directory") { item = new DirectoryVolumeItem(db); counters[TOTAL_DIRS]++; } else { item = new FileVolumeItem(db); long size = (long)reader["size"]; ((FileVolumeItem)item).SetFileVolumeItemFields(size, null); counters[TOTAL_FILES]++; counters[TOTAL_SIZE] += size; } string path = (string)reader["path"]; Debug.Assert(path.StartsWith("file://"), "path starts with 'file://'"); string name = (string)reader["name"]; string location = DecoderUtility.UrlDecode(path); location = location.Substring(rootPath.Length); location = location.Substring(0, location.Length - name.Length - 1); if (location.Length == 0) location = "/"; long itemID = 2 + (long)reader["id"] - minFileID; // id 1 is the root item long parentID = Math.Max(1, 2 + (long)reader["idparent"] - minFileID); item.SetFileSystemVolumeItemFields(location, DateTime.MinValue, VolumeDatabase.ID_NONE); item.SetVolumeItemFields(volumeID, itemID, parentID, name, Util.ReplaceDBNull<string>(reader["mime"], null), metaData, Util.ReplaceDBNull<string>(reader["comment"], null), null); writer.Write(item); }
private void ScanningThread(PlatformIO.DriveInfo drive, TVolume volume, BufferedVolumeItemWriter writer) { TVolume returnVolume = null; Exception fatalError = null; bool cancelled = false; try { if (this.HasDB) { Database.TransactionBegin(); // locks VolumeDatabase } ScanningThreadMain(drive, volume, writer); if (this.HasDB) { writer.Close(); if (!volume.IsInserted) { volume.InsertIntoDB(); } returnVolume = volume; database.TransactionCommit(); // unlocks VolumeDatabase } //result = ScanningResult.Success; scanSucceeded = true; } catch (Exception ex) { Exception rollbackException = null; try { // rollback all database changes if (this.HasDB) { database.TransactionRollback(); // unlocks VolumeDatabase } } catch (Exception e) { rollbackException = e; } if (ex is ScanCancelledException) { //result = ScanningResult.Cancelled; cancelled = true; } else { //result = ScanningResult.FatalError; /* save the error that caused the scanner to stop (scanning failure) */ fatalError = ex; //OnError(new ErrorEventArgs(ex)); PostError(ex); Debug.WriteLine("Details for exception in ScanningThread():\n" + ex.ToString()); } // in case an error occured while rollig back, // post the error here, _after_ the initial error that made the scan fail. if (rollbackException != null) { //OnError(new ErrorEventArgs(rollbackException)); PostError(rollbackException); } //#if THROW_EXCEPTIONS_ON_ALL_THREADS // if (!(ex is ScanCancelledException)) // throw; //#endif } finally { /* * TODO : unlock db / thread // (in try / catch / PostError !!) */ //if (result == ScanningResult.Success) // m_scanSucceeded = true; //m_cancellationRequested = false; isRunning = false; //try { OnScanCompleted(new ScanCompletedEventArgs(result, mediaID, fatalError)); } //catch (Exception e) { OnError(new ErrorEventArgs(e)); } PostCompleted(returnVolume, fatalError, cancelled); } }
private void RecursiveDump(string rootPath, DirectoryInfo dir, BufferedVolumeItemWriter writer, long parentID) { CheckForCancellationRequest(); /* event is called before a _directory_ item is about to be scanned only. * it could also be called everytime a _file_ item is about to be scanned, * but this could result in a performance loss. */ PostBeforeScanItem(dir.FullName); //OnBeforeScanItem(new BeforeScanItemEventArgs(dir.FullName)); // bool dirIsSymLink = false; // string symLinkTarget = null; FileType ft; // catch possible FileNotFoundExceptions // (e.g. on filesystems with wrong filename encoding or vanishing virtual files in /dev). try { ft = FileHelper.GetFileType(dir.FullName, false); } catch (FileNotFoundException ex) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Directory '{0}' not found. (Wrong filename encoding?)"), dir.FullName), ex); return; } bool dirIsSymLink = (ft == FileType.SymbolicLink); if ((ft != FileType.Directory) && !dirIsSymLink) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Skipped item '{0}' as it doesn't seem to be a real directory."), dir.FullName)); return; } if (dirIsSymLink) { if (!Options.DiscardSymLinks) { string symLinkTarget = null; try { // get real path with all symlinks resolved symLinkTarget = FileHelper .GetCanonicalSymLinkTarget(dir.FullName); } catch (FileNotFoundException) {} // Note: // this check seems to be useless since a broken link // to a directory is identified as a broken link to a _file_ (a few lines below). if (symLinkTarget == null) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Skipped symlink item '{0}' as the target does not exist."), dir.FullName)); return; } // skip symlinks outside of rootPath // (in addition, GetLocation()/FixPath() need paths relative to rootPath) if (!symLinkTarget.StartsWith(rootPath)) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Skipped symlink item '{0}' as it appears to point to a different drive ('{1}')."), dir.FullName, symLinkTarget)); return; } symLinkHelper.AddSymLink(dir, symLinkTarget, rootPath, parentID, true); } /* do not dump symlinks to directories */ return; } /* insert dirname */ long dirID = InsertDir(rootPath, dir, writer, parentID); parentID = dirID; // TODO : check m_cancel here (?) // /* do not dump symlinks to directories */ // if (dirIsSymlink) // return; try { /* insert files of dir */ FileInfo[] files = dir.GetFiles(); /* throws access exceptions (cant access _DIRECTORY_) */ for (int i = 0; i < files.Length; i++) { CheckForCancellationRequest(); // bool isRegularFile = true; // bool isSymLink = false; //#if DEBUG && DEBUG_FILE_VERBOSE if (Global.EnableDebugging) { Debug.WriteLine(string.Format("Indexing file '{0}'", files[i].FullName)); } //#endif // catch possible FileNotFoundExceptions // (e.g. on filesystems with wrong filename encoding or vanishing virtual files in /dev). try { ft = FileHelper.GetFileType(files[i].FullName, false); } catch (FileNotFoundException ex) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("File '{0}' not found. (Wrong filename encoding?)"), files[i].FullName), ex); continue; } /* special files (fifos, blockdevices, chardevices) are skipped */ bool isRegularFile = (ft == FileType.RegularFile); bool isSymLink = (ft == FileType.SymbolicLink); if (isRegularFile) { string mimeType = null; MetadataStore metaData = MetadataStore.Empty; string hash = null; bool thumbGenerated = false; FileStream fs = null; try { // OpenRead() must be called _before_ MimeInfo.GetMimeType(), // since this method returns a mimetype even if the file does not exist / can't be accessed. fs = File.OpenRead(files[i].FullName); /* throws access/IO exceptions (cant access _FILE_) */ mimeType = MimeType.GetMimeTypeForFile(files[i].FullName); if (Options.MetadataProviders != null) { IEnumerable <MetadataItem> items = null; foreach (MetadataProvider mdp in Options.MetadataProviders) { IEnumerable <MetadataItem> tmp = null; try { tmp = mdp.GetMetadata(files[i].FullName, mimeType); } catch (Exception e) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Extracting metadata from file '{0}' failed. ({1})"), files[i].FullName, e.Message), e); } if (items == null) { items = tmp; } else { if (tmp != null) { items = items.Concat(tmp); } } } metaData = new MetadataStore(items); } if (Options.ComputeHashs) { hash = ComputeHash(fs); // TODO : check m_cancel here? hashing can be a lengthy operation on big files. } if (Options.GenerateThumbnails) { thumbGenerated = thumbGen.GenerateThumbnail(files[i], mimeType); } } catch (Exception e) { // ### exception caught: hash, mime and/or metadata may be null // and the thumbnail may not have been generated! if (e is UnauthorizedAccessException || e is IOException) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Error opening file '{0}', can't retrieve any mime/metadata. ({1})"), files[i].FullName, e.Message), e); } else { throw; } } finally { if (fs != null) { fs.Close(); } } long fileID = InsertFile(rootPath, files[i], writer, parentID, mimeType, metaData, hash); if (thumbGenerated) { thumbGen.SaveThumbnail(Path.Combine(paths.thumbnailPath, string.Format("{0}.png", fileID))); } } else if (isSymLink) { if (!Options.DiscardSymLinks) { string symLinkTarget = null; try { // get real path with all symlinks resolved symLinkTarget = FileHelper .GetCanonicalSymLinkTarget(files[i].FullName); } catch (FileNotFoundException) {} if (symLinkTarget == null) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Skipped symlink item '{0}' as the target does not exist."), files[i].FullName)); // skip symlinks outside of rootPath // (in addition, GetLocation()/FixPath() need paths relative to rootPath) } else if (!symLinkTarget.StartsWith(rootPath)) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Skipped symlink item '{0}' as it appears to point to a different drive ('{1}')."), files[i].FullName, symLinkTarget)); // skip symlinks pointing to special files (only regular files are indexed) } else if (FileHelper.GetFileType(symLinkTarget, false) != FileType.RegularFile) { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Skipped symlink item '{0}' as it does not point to a regular file ('{1}')."), files[i].FullName, symLinkTarget)); } else { symLinkHelper.AddSymLink(files[i], symLinkTarget, rootPath, parentID, false); } } } else { /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Skipped item '{0}' as it appears to be some kind of special file."), files[i].FullName)); } // TODO : check m_cancel here (?) } // end for /* recursively dump subdirs */ DirectoryInfo[] childDirs = dir.GetDirectories(); /* throws access exceptions (cant access _DIRECTORY_) */ for (int i = 0; i < childDirs.Length; i++) { RecursiveDump(rootPath, childDirs[i], writer, parentID); } } catch (UnauthorizedAccessException e) { //ScannerWarningEventArgs args = new ScannerWarningEventArgs("Unable to dump dir '" + dir.FullName + "'. (" + e.Message + ")", e); //OnScannerWarning(args); // may throw ScanCancelledException /* may throw ScanCancelledException */ SendScannerWarning(string.Format(S._("Unable to dump dir '{0}'. ({1})"), dir.FullName, e.Message), e); } }