src/FilesystemCommand.h
branchv_0
changeset 4 d44ed75822e7
parent 3 62eac7ab4cf4
child 7 8d73bff730a7
equal deleted inserted replaced
3:62eac7ab4cf4 4:d44ed75822e7
     1 /**
     1 /**
     2  * Relational pipes
     2  * Relational pipes
     3  * Copyright © 2018 František Kučera (Frantovo.cz, GlobalCode.info)
     3  * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
     4  *
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, either version 3 of the License, or
     7  * the Free Software Foundation, either version 3 of the License, or
     8  * (at your option) any later version.
     8  * (at your option) any later version.
    17  */
    17  */
    18 #pragma once
    18 #pragma once
    19 
    19 
    20 #include <cstdlib>
    20 #include <cstdlib>
    21 #include <iostream>
    21 #include <iostream>
       
    22 #include <sstream>
    22 #include <string>
    23 #include <string>
    23 #include <vector>
    24 #include <vector>
    24 #include <array>
    25 #include <map>
    25 #include <algorithm>
    26 #include <algorithm>
    26 #include <filesystem>
    27 #include <filesystem>
    27 
    28 
    28 #include <pwd.h>
    29 #include <pwd.h>
    29 #include <grp.h>
    30 #include <grp.h>
    31 
    32 
    32 #include <sys/xattr.h>
    33 #include <sys/xattr.h>
    33 
    34 
    34 #include <relpipe/writer/typedefs.h>
    35 #include <relpipe/writer/typedefs.h>
    35 
    36 
       
    37 #include "Configuration.h"
       
    38 #include "AttributeFinder.h"
       
    39 #include "FileAttributeFinder.h"
       
    40 #include "XattrAttributeFinder.h"
       
    41 
    36 namespace relpipe {
    42 namespace relpipe {
    37 namespace in {
    43 namespace in {
    38 namespace filesystem {
    44 namespace filesystem {
    39 
    45 
    40 namespace fs = std::filesystem;
    46 namespace fs = std::filesystem;
    41 using namespace relpipe::writer;
    47 using namespace relpipe::writer;
    42 
    48 
    43 class FilesystemCommand {
    49 class FilesystemCommand {
       
    50 private:
    44 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
    51 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
       
    52 
       
    53 	FileAttributeFinder fileAttributeFinder;
       
    54 	XattrAttributeFinder xattrAttributeFinder;
       
    55 
       
    56 	std::map<string_t, AttributeFinder*> attributeFinders{
       
    57 		{RequestedField::GROUP_FILE, &fileAttributeFinder},
       
    58 		{RequestedField::GROUP_XATTR, &xattrAttributeFinder}};
    45 
    59 
    46 	void reset(std::stringstream& stream) {
    60 	void reset(std::stringstream& stream) {
    47 		stream.str("");
    61 		stream.str("");
    48 		stream.clear();
    62 		stream.clear();
    49 	}
    63 	}
    54 			else originalName << ch;
    68 			else originalName << ch;
    55 		}
    69 		}
    56 		return originalName.tellp();
    70 		return originalName.tellp();
    57 	}
    71 	}
    58 
    72 
    59 	string_t getType(const fs::path& file) {
       
    60 		// TODO: Use whole words? (letters are compatible with find -type)
       
    61 		if (fs::is_regular_file(file)) return L"f";
       
    62 		else if (fs::is_symlink(file)) return L"l"; // symlinks to directories are both symlinks and directories
       
    63 		else if (fs::is_directory(file)) return L"d";
       
    64 		else if (fs::is_fifo(file)) return L"p";
       
    65 		else if (fs::is_socket(file)) return L"s";
       
    66 		else if (fs::is_block_file(file)) return L"b";
       
    67 		else if (fs::is_character_file(file)) return L"c";
       
    68 		else return L"o";
       
    69 	}
       
    70 
       
    71 	void fetchOwner(const fs::path& file, string_t& owner, string_t& group) {
       
    72 		// TODO: throw exception on error
       
    73 		// TODO: get user and group in C++ way?
       
    74 		struct stat info;
       
    75 		stat(file.c_str(), &info);
       
    76 		/**
       
    77 		 * The return value may point to a static area, and may  be
       
    78 		 * overwritten  by  subsequent calls to getpwent(3), getpw‐
       
    79 		 * nam(), or getpwuid().  (Do not pass the returned pointer
       
    80 		 * to free(3).)
       
    81 		 */
       
    82 		struct passwd* pw = getpwuid(info.st_uid);
       
    83 		struct group* gr = getgrgid(info.st_gid);
       
    84 		owner = convertor.from_bytes(pw->pw_name);
       
    85 		group = convertor.from_bytes(gr->gr_name);
       
    86 	}
       
    87 
       
    88 	string_t getXattr(const fs::path& file, const string_t& attributeName) {
       
    89 		// TODO: review, test on multiple platforms
       
    90 		// TODO: improve documentation in xattr.h in glibc
       
    91 		// TODO: check XATTR_NAME_MAX in limits.h and if it is reasonably small on our system, allocate buffer for it instead of reading twice
       
    92 		// TODO: avoid race condition (another process might change the attribute between reading its length and its value)
       
    93 		ssize_t length = getxattr(file.c_str(), convertor.to_bytes(attributeName).c_str(), nullptr, 0);
       
    94 		if (length > 0) {
       
    95 			std::vector<char> buffer(length + 1);
       
    96 			getxattr(file.c_str(), convertor.to_bytes(attributeName).c_str(), buffer.data(), buffer.size());
       
    97 			return convertor.from_bytes(buffer.data());
       
    98 		} else {
       
    99 			return L"";
       
   100 		}
       
   101 	}
       
   102 
       
   103 public:
    73 public:
   104 
    74 
   105 	void process(std::istream& input, std::ostream& output) {
    75 	void process(std::istream& input, std::ostream& output, Configuration& configuration) {
   106 		std::shared_ptr<RelationalWriter> writer(Factory::create(output));
    76 		std::shared_ptr<RelationalWriter> writer(Factory::create(output));
   107 
    77 
   108 		// TODO: redesign: parametrized from CLI + groups of information fetched together and used for multiple attributes
       
   109 		bool enableName = true;
       
   110 		bool enableExists = false;
       
   111 		bool enableNameAbsolute = false;
       
   112 		bool enableNameCanonical = false;
       
   113 		bool enableType = true;
       
   114 		bool enableSize = true;
       
   115 		bool enableOwner = true;
       
   116 		bool enableGroup = true;
       
   117 		bool enableXattrUrl = true; // just a demo
       
   118 
       
   119 		std::vector<AttributeMetadata> attributesMetadata;
    78 		std::vector<AttributeMetadata> attributesMetadata;
   120 		if (enableName) attributesMetadata.push_back({L"name", TypeId::STRING});
    79 		for (RequestedField field : configuration.fields) {
   121 		if (enableExists) attributesMetadata.push_back({L"exists", TypeId::BOOLEAN});
    80 			AttributeFinder* finder = attributeFinders[field.group];
   122 		if (enableNameAbsolute) attributesMetadata.push_back({L"name_absolute", TypeId::STRING});
    81 			if (finder) for (AttributeMetadata m : finder->toMetadata(field)) attributesMetadata.push_back(m);
   123 		if (enableNameCanonical) attributesMetadata.push_back({L"name_canonical", TypeId::STRING});
    82 			else throw RelpipeWriterException(L"Unsupported field group: " + field.group);
   124 		if (enableType) attributesMetadata.push_back({L"type", TypeId::STRING});
    83 		}
   125 		if (enableSize) attributesMetadata.push_back({L"size", TypeId::INTEGER});
       
   126 		if (enableOwner) attributesMetadata.push_back({L"owner", TypeId::STRING});
       
   127 		if (enableGroup) attributesMetadata.push_back({L"group", TypeId::STRING});
       
   128 		if (enableXattrUrl) attributesMetadata.push_back({L"xattr_url", TypeId::STRING});
       
   129 
    84 
   130 		writer->startRelation(L"filesystem", attributesMetadata, true);
    85 		writer->startRelation(L"filesystem", attributesMetadata, true);
   131 
    86 
       
    87 
   132 		for (std::stringstream originalName; readNext(input, originalName); reset(originalName)) {
    88 		for (std::stringstream originalName; readNext(input, originalName); reset(originalName)) {
   133 			if (enableName) writer->writeAttribute(convertor.from_bytes(originalName.str()));
    89 
   134 			fs::path file(originalName.str());
    90 			fs::path file(originalName.str());
   135 			bool exists = false;
    91 			bool exists = false;
   136 
    92 
   137 			try {
    93 			try {
   138 				exists = fs::exists(file);
    94 				exists = fs::exists(file);
   139 			} catch (const fs::filesystem_error& e) {
    95 			} catch (const fs::filesystem_error& e) {
   140 				// we probably do not have permissions to given directory → pretend that the file does not exist
    96 				// we probably do not have permissions to given directory → pretend that the file does not exist
   141 			}
    97 			}
   142 
    98 
   143 			if (exists) {
    99 			for (auto& finder : attributeFinders) finder.second->startFile(file);
   144 				if (enableExists) writer->writeAttribute(L"true");
       
   145 				if (enableNameAbsolute) writer->writeAttribute(fs::absolute(file).wstring());
       
   146 				if (enableNameCanonical) writer->writeAttribute(fs::canonical(file).wstring());
       
   147 				if (enableType) writer->writeAttribute(getType(file));
       
   148 				if (enableSize) {
       
   149 					integer_t size = fs::is_regular_file(file) ? fs::file_size(file) : 0;
       
   150 					writer->writeAttribute(&size, typeid (size));
       
   151 				}
       
   152 
   100 
   153 				if (enableOwner || enableGroup) {
   101 			for (RequestedField field : configuration.fields) {
   154 					string_t owner;
   102 				AttributeFinder* finder = attributeFinders[field.group]; // should not be nullptr, because already checked while writing the relation metadata
   155 					string_t group;
       
   156 					fetchOwner(file, owner, group);
       
   157 
   103 
   158 					if (enableOwner) writer->writeAttribute(owner);
   104 				if (exists || (field.group == RequestedField::GROUP_FILE && field.name == FileAttributeFinder::FIELD_PATH_ORIGINAL)) {
   159 					if (enableGroup) writer->writeAttribute(group);
   105 					finder->writeField(writer.get(), field);
   160 				}
   106 				} else {
   161 
   107 					finder->writeEmptyField(writer.get(), field);
   162 				if (enableXattrUrl) writer->writeAttribute(getXattr(file, L"user.xdg.origin.url"));
       
   163 
       
   164 			} else {
       
   165 				// Only original name was written → write remaining attributes
       
   166 				// TODO: null values (requires adding null support to the format specification)
       
   167 				for (int i = 1; i < attributesMetadata.size(); i++) {
       
   168 					switch (attributesMetadata[i].typeId) {
       
   169 						case TypeId::BOOLEAN: writer->writeAttribute(L"false");
       
   170 							break;
       
   171 						case TypeId::INTEGER: writer->writeAttribute(L"0");
       
   172 							break;
       
   173 						case TypeId::STRING: writer->writeAttribute(L"");
       
   174 							break;
       
   175 						default:
       
   176 							throw RelpipeWriterException(L"Unsupported data type.");
       
   177 
       
   178 					}
       
   179 				}
   108 				}
   180 			}
   109 			}
       
   110 			
       
   111 			for (auto& finder : attributeFinders) finder.second->endFile();
   181 		}
   112 		}
   182 
       
   183 	}
   113 	}
   184 };
   114 };
   185 
   115 
   186 }
   116 }
   187 }
   117 }