process also links to non-existent files and non-readable links v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Wed, 16 Jan 2019 19:31:37 +0100
branchv_0
changeset 9 b4f29fb16159
parent 8 eb1ecb37c6b7
child 10 3e1608320b6c
process also links to non-existent files and non-readable links
src/AttributeFinder.h
src/FileAttributeFinder.h
src/FilesystemCommand.h
src/XattrAttributeFinder.h
--- a/src/AttributeFinder.h	Wed Jan 16 18:19:50 2019 +0100
+++ b/src/AttributeFinder.h	Wed Jan 16 19:31:37 2019 +0100
@@ -37,41 +37,14 @@
 protected:
 	fs::path currentFile;
 	string currentFileRaw;
-public:
-
-	/**
-	 * Single requested fields might generate multiple attributes in the relation.
-	 * But usually it is 1:1.
-	 * @param field requested field from the user (usually from CLI arguments)
-	 * @return attribute metadata to be used in the RelationalWriter.startRelation()
-	 */
-	virtual vector<AttributeMetadata> toMetadata(const RequestedField& field) = 0;
-
-	/**
-	 * Writing of the record for current file is starting.
-	 * Following writeField() calls are related to this file.
-	 * @param file path to the file
-	 * @param fileRaw raw file name as it was on the input
-	 */
-	virtual void startFile(const fs::path& file, const string& fileRaw) {
-		currentFile = file;
-		currentFileRaw = fileRaw;
-	}
-
-	/**
-	 * Writing of the record for current file is finished. All resources linked to this file should be released.
-	 */
-	virtual void endFile() {
-		currentFile.clear();
-		currentFileRaw.clear();
-	}
+	bool currentFileExists;
 
 	/**
 	 * Writes field attribute(s). The attribute count must match with count of AttributeMetadata returned in toMetadata().
 	 * @param writer
 	 * @param field
 	 */
-	virtual void writeField(RelationalWriter* writer, const RequestedField& field) = 0;
+	virtual void writeFieldOfExistingFile(RelationalWriter* writer, const RequestedField& field) = 0;
 
 	/**
 	 * Writes empty attribute(s) in case of non-existent file or an error. 
@@ -98,6 +71,41 @@
 		}
 	}
 
+public:
+
+	/**
+	 * Single requested fields might generate multiple attributes in the relation.
+	 * But usually it is 1:1.
+	 * @param field requested field from the user (usually from CLI arguments)
+	 * @return attribute metadata to be used in the RelationalWriter.startRelation()
+	 */
+	virtual vector<AttributeMetadata> toMetadata(const RequestedField& field) = 0;
+
+	/**
+	 * Writing of the record for current file is starting.
+	 * Following writeField() calls are related to this file.
+	 * @param file path to the file
+	 * @param fileRaw raw file name as it was on the input
+	 */
+	virtual void startFile(const fs::path& file, const string& fileRaw, bool exists) {
+		currentFile = file;
+		currentFileRaw = fileRaw;
+		currentFileExists = exists;
+	}
+
+	virtual void writeField(RelationalWriter* writer, const RequestedField& field) {
+		if (currentFileExists) writeFieldOfExistingFile(writer, field);
+		else writeEmptyField(writer, field);
+	}
+
+	/**
+	 * Writing of the record for current file is finished. All resources linked to this file should be released.
+	 */
+	virtual void endFile() {
+		currentFile.clear();
+		currentFileRaw.clear();
+	}
+
 	virtual ~AttributeFinder() {
 	}
 };
--- a/src/FileAttributeFinder.h	Wed Jan 16 18:19:50 2019 +0100
+++ b/src/FileAttributeFinder.h	Wed Jan 16 19:31:37 2019 +0100
@@ -41,9 +41,9 @@
 	string_t currentOwner;
 	string_t currentGroup;
 
-	string_t getType(const fs::path& file, bool x) {
+	string_t getType(const fs::path& file, bool workWithSymlinkItself) {
 		// TODO: Use whole words? (letters are compatible with find -type)
-		if (fs::is_symlink(file) && x) return L"l"; // symlinks are both symlinks and files/directories/etc.
+		if (fs::is_symlink(file) && workWithSymlinkItself) return L"l"; // symlinks are both symlinks and files/directories/etc.
 		else if (fs::is_regular_file(file)) return L"f";
 		else if (fs::is_directory(file)) return L"d";
 		else if (fs::is_fifo(file)) return L"p";
@@ -69,6 +69,50 @@
 		owner = convertor.from_bytes(pw->pw_name);
 		group = convertor.from_bytes(gr->gr_name);
 	}
+
+protected:
+
+	virtual void writeFieldOfExistingFile(RelationalWriter* writer, const RequestedField& field) override {
+		if (field.group == RequestedField::GROUP_FILE) {
+			for (string_t alias : field.getAliases()) {
+				if (field.name == FIELD_NAME) {
+					writer->writeAttribute(currentFile.filename().wstring());
+				} else if (field.name == FIELD_PATH_ORIGINAL) {
+					writer->writeAttribute(convertor.from_bytes(currentFileRaw));
+				} else if (field.name == FIELD_PATH_ABSOLUTE) {
+					writer->writeAttribute(fs::absolute(currentFile).wstring());
+				} else if (field.name == FIELD_PATH_CANONICAL) {
+					writer->writeAttribute(fs::canonical(currentFile).wstring());
+				} else if (field.name == FIELD_TYPE) {
+					writer->writeAttribute(getType(currentFile, true));
+				} else if (field.name == FIELD_SYMLINK_TARGET_TYPE) {
+					writer->writeAttribute(getType(currentFile, false));
+				} else if (field.name == FIELD_SYMLINK_TARGET) {
+					string_t symlinkTarget; // TODO: null value (when supported)
+					if (fs::is_symlink(currentFile)) {
+						try {
+							symlinkTarget = fs::read_symlink(currentFile).wstring();
+						} catch (...) {
+							// missing permissions, not readable → leave empty
+						}
+					}
+					writer->writeAttribute(symlinkTarget);
+				} else if (field.name == FIELD_SIZE) {
+					integer_t size = fs::is_regular_file(currentFile) ? fs::file_size(currentFile) : 0;
+					writer->writeAttribute(&size, typeid (size));
+				} else if (field.name == FIELD_OWNER) {
+					if (currentOwner.empty()) fetchOwner(currentFile, currentOwner, currentGroup);
+					writer->writeAttribute(currentOwner);
+				} else if (field.name == FIELD_GROUP) {
+					if (currentOwner.empty()) fetchOwner(currentFile, currentOwner, currentGroup);
+					writer->writeAttribute(currentGroup);
+				} else {
+					throw RelpipeWriterException(L"Unsupported field name in FileAttributeFinder: " + field.name);
+				}
+			}
+		}
+	}
+
 public:
 
 	static const string_t FIELD_PATH_ORIGINAL;
@@ -95,46 +139,21 @@
 		}
 	}
 
+	void writeField(RelationalWriter* writer, const RequestedField& field) override {
+		if (currentFileExists
+				|| field.name == FIELD_PATH_ORIGINAL
+				|| (fs::is_symlink(currentFile) && (field.name == FIELD_SYMLINK_TARGET || field.name == FIELD_TYPE)))
+			writeFieldOfExistingFile(writer, field);
+		else
+			writeEmptyField(writer, field);
+	}
+
 	void endFile() override {
 		AttributeFinder::endFile();
 		currentOwner.clear();
 		currentGroup.clear();
 	};
 
-	virtual void writeField(RelationalWriter* writer, const RequestedField& field) override {
-		if (field.group == RequestedField::GROUP_FILE) {
-			for (string_t alias : field.getAliases()) {
-				if (field.name == FIELD_NAME) {
-					writer->writeAttribute(currentFile.filename().wstring());
-				} else if (field.name == FIELD_PATH_ORIGINAL) {
-					writer->writeAttribute(convertor.from_bytes(currentFileRaw));
-				} else if (field.name == FIELD_PATH_ABSOLUTE) {
-					writer->writeAttribute(fs::absolute(currentFile).wstring());
-				} else if (field.name == FIELD_PATH_CANONICAL) {
-					writer->writeAttribute(fs::canonical(currentFile).wstring());
-				} else if (field.name == FIELD_TYPE) {
-					writer->writeAttribute(getType(currentFile, true));
-				} else if (field.name == FIELD_SYMLINK_TARGET_TYPE) {
-					writer->writeAttribute(getType(currentFile, false));
-				} else if (field.name == FIELD_SYMLINK_TARGET) {
-					writer->writeAttribute(fs::is_symlink(currentFile) ? fs::read_symlink(currentFile).wstring() : L""); // TODO: null value (when supported)
-				} else if (field.name == FIELD_SIZE) {
-					integer_t size = fs::is_regular_file(currentFile) ? fs::file_size(currentFile) : 0;
-					writer->writeAttribute(&size, typeid (size));
-				} else if (field.name == FIELD_OWNER) {
-					if (currentOwner.empty()) fetchOwner(currentFile, currentOwner, currentGroup);
-					writer->writeAttribute(currentOwner);
-				} else if (field.name == FIELD_GROUP) {
-					if (currentOwner.empty()) fetchOwner(currentFile, currentOwner, currentGroup);
-					writer->writeAttribute(currentGroup);
-				} else {
-					// TODO: should not happend; check supported attributes in toMetadata()?
-					writer->writeAttribute(L"");
-				}
-			}
-		}
-	}
-
 	virtual ~FileAttributeFinder() override {
 	}
 };
--- a/src/FilesystemCommand.h	Wed Jan 16 18:19:50 2019 +0100
+++ b/src/FilesystemCommand.h	Wed Jan 16 19:31:37 2019 +0100
@@ -95,17 +95,11 @@
 				// we probably do not have permissions to given directory → pretend that the file does not exist
 			}
 
-			for (auto& finder : attributeFinders) finder.second->startFile(file, originalName.str());
+			for (auto& finder : attributeFinders) finder.second->startFile(file, originalName.str(), exists);
 
 			for (RequestedField field : configuration.fields) {
 				AttributeFinder* finder = attributeFinders[field.group]; // should not be nullptr, because already checked while writing the relation metadata
-
-				// TODO: links to non-existent files are currently treated as non-existent files → we still can return the type and symlink_target values
-				if (exists || (field.group == RequestedField::GROUP_FILE && field.name == FileAttributeFinder::FIELD_PATH_ORIGINAL)) {
-					finder->writeField(writer.get(), field);
-				} else {
-					finder->writeEmptyField(writer.get(), field);
-				}
+				finder->writeField(writer.get(), field);
 			}
 
 			for (auto& finder : attributeFinders) finder.second->endFile();
--- a/src/XattrAttributeFinder.h	Wed Jan 16 18:19:50 2019 +0100
+++ b/src/XattrAttributeFinder.h	Wed Jan 16 19:31:37 2019 +0100
@@ -51,6 +51,14 @@
 			return L"";
 		}
 	}
+protected:
+
+	virtual void writeFieldOfExistingFile(RelationalWriter* writer, const RequestedField& field) override {
+		for (string_t alias : field.getAliases()) {
+			if (field.group == RequestedField::GROUP_XATTR) writer->writeAttribute(getXattr(currentFile, field.name));
+		}
+	}
+
 public:
 
 	virtual vector<AttributeMetadata> toMetadata(const RequestedField& field) override {
@@ -63,12 +71,6 @@
 		}
 	}
 
-	virtual void writeField(RelationalWriter* writer, const RequestedField& field) override {
-		for (string_t alias : field.getAliases()) {
-			if (field.group == RequestedField::GROUP_XATTR) writer->writeAttribute(getXattr(currentFile, field.name));
-		}
-	}
-
 	virtual ~XattrAttributeFinder() override {
 	}
 };