1a38a92c9SIngo Weinhold/*
245eb999eSIngo Weinhold * Copyright 2007-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3a38a92c9SIngo Weinhold * Distributed under the terms of the MIT License.
4a38a92c9SIngo Weinhold */
5a38a92c9SIngo Weinhold
6360a6446SAxel Dörfler
7a3615ef1SIngo Weinhold#include "compatibility.h"
890c08768SIngo Weinhold
9ada99c21SIngo Weinhold#include "fssh.h"
10ada99c21SIngo Weinhold
11ada99c21SIngo Weinhold#include <stdarg.h>
1290c08768SIngo Weinhold#include <stdio.h>
1390c08768SIngo Weinhold#include <string.h>
14ada99c21SIngo Weinhold#include <time.h>
15a3615ef1SIngo Weinhold#include <unistd.h>
16cbd4dd19SIngo Weinhold#include <stdlib.h>
17ada99c21SIngo Weinhold
18ada99c21SIngo Weinhold#include <vector>
1990c08768SIngo Weinhold
201137bb03SIngo Weinhold#include "command_cp.h"
21cad7120fSIngo Weinhold#include "driver_settings.h"
22f6233d23SIngo Weinhold#include "external_commands.h"
23ada99c21SIngo Weinhold#include "fd.h"
24ada99c21SIngo Weinhold#include "fssh_dirent.h"
252842773aSIngo Weinhold#include "fssh_errno.h"
2690c08768SIngo Weinhold#include "fssh_errors.h"
27114d7934SAxel Dörfler#include "fssh_fs_info.h"
287f46de0aSAugustin Cavalier#include "fssh_fcntl.h"
2990c08768SIngo Weinhold#include "fssh_module.h"
305e2ef462SAxel Dörfler#include "fssh_node_monitor.h"
31ada99c21SIngo Weinhold#include "fssh_stat.h"
32ada99c21SIngo Weinhold#include "fssh_string.h"
334f7504e3SIngo Weinhold#include "fssh_type_constants.h"
3490c08768SIngo Weinhold#include "module.h"
353e617040SIngo Weinhold#include "partition_support.h"
364f7504e3SIngo Weinhold#include "path_util.h"
3790c08768SIngo Weinhold#include "syscalls.h"
3890c08768SIngo Weinhold#include "vfs.h"
3990c08768SIngo Weinhold
4090c08768SIngo Weinhold
41ada99c21SIngo Weinholdextern fssh_module_info *modules[];
42ada99c21SIngo Weinhold
4390c08768SIngo Weinhold
44ada99c21SIngo Weinholdextern fssh_file_system_module_info gRootFileSystem;
45ada99c21SIngo Weinhold
466f057874SIngo Weinholdnamespace FSShell {
476f057874SIngo Weinhold
48ada99c21SIngo Weinholdconst char* kMountPoint = "/myfs";
4990c08768SIngo Weinhold
50f6233d23SIngo Weinhold// command line args
51f6233d23SIngo Weinholdstatic	int					sArgc;
52f6233d23SIngo Weinholdstatic	const char* const*	sArgv;
53f6233d23SIngo Weinhold
54f9bf1195SIngo Weinholdstatic mode_t sUmask = 0022;
55f9bf1195SIngo Weinhold
5690c08768SIngo Weinhold
5790c08768SIngo Weinholdstatic fssh_status_t
5890c08768SIngo Weinholdinit_kernel()
5990c08768SIngo Weinhold{
6090c08768SIngo Weinhold	fssh_status_t error;
6190c08768SIngo Weinhold
6290c08768SIngo Weinhold	// init module subsystem
6390c08768SIngo Weinhold	error = module_init(NULL);
6490c08768SIngo Weinhold	if (error != FSSH_B_OK) {
65ada99c21SIngo Weinhold		fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error));
6690c08768SIngo Weinhold		return error;
67f6233d23SIngo Weinhold	}
6890c08768SIngo Weinhold
69cad7120fSIngo Weinhold	// init driver settings
70cad7120fSIngo Weinhold	error = driver_settings_init();
71cad7120fSIngo Weinhold	if (error != FSSH_B_OK) {
72cad7120fSIngo Weinhold		fprintf(stderr, "initializing driver settings failed: %s\n",
73cad7120fSIngo Weinhold			fssh_strerror(error));
74cad7120fSIngo Weinhold		return error;
75cad7120fSIngo Weinhold	}
76cad7120fSIngo Weinhold
7790c08768SIngo Weinhold	// register built-in modules, i.e. the rootfs and the client FS
7890c08768SIngo Weinhold	register_builtin_module(&gRootFileSystem.info);
7990c08768SIngo Weinhold	for (int i = 0; modules[i]; i++)
8090c08768SIngo Weinhold		register_builtin_module(modules[i]);
8190c08768SIngo Weinhold
8290c08768SIngo Weinhold	// init VFS
8390c08768SIngo Weinhold	error = vfs_init(NULL);
8490c08768SIngo Weinhold	if (error != FSSH_B_OK) {
85ada99c21SIngo Weinhold		fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error));
8690c08768SIngo Weinhold		return error;
87f6233d23SIngo Weinhold	}
8890c08768SIngo Weinhold
89ada99c21SIngo Weinhold	// init kernel IO context
90ada99c21SIngo Weinhold	gKernelIOContext = (io_context*)vfs_new_io_context(NULL);
91ada99c21SIngo Weinhold	if (!gKernelIOContext) {
92ada99c21SIngo Weinhold		fprintf(stderr, "creating IO context failed!\n");
93ada99c21SIngo Weinhold		return FSSH_B_NO_MEMORY;
94ada99c21SIngo Weinhold	}
95ada99c21SIngo Weinhold
9690c08768SIngo Weinhold	// mount root FS
9790c08768SIngo Weinhold	fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0);
9890c08768SIngo Weinhold	if (rootDev < 0) {
99ada99c21SIngo Weinhold		fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev));
10090c08768SIngo Weinhold		return rootDev;
101f6233d23SIngo Weinhold	}
10290c08768SIngo Weinhold
1035e2ef462SAxel Dörfler	// set cwd to "/"
104ada99c21SIngo Weinhold	error = _kern_setcwd(-1, "/");
105ada99c21SIngo Weinhold	if (error != FSSH_B_OK) {
106ada99c21SIngo Weinhold		fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error));
107ada99c21SIngo Weinhold		return error;
108f6233d23SIngo Weinhold	}
109ada99c21SIngo Weinhold
11090c08768SIngo Weinhold	// create mount point for the client FS
111ada99c21SIngo Weinhold	error = _kern_create_dir(-1, kMountPoint, 0775);
11290c08768SIngo Weinhold	if (error != FSSH_B_OK) {
113ada99c21SIngo Weinhold		fprintf(stderr, "creating mount point failed: %s\n",
114ada99c21SIngo Weinhold			fssh_strerror(error));
11590c08768SIngo Weinhold		return error;
116f6233d23SIngo Weinhold	}
11790c08768SIngo Weinhold
11890c08768SIngo Weinhold	return FSSH_B_OK;
11990c08768SIngo Weinhold}
12090c08768SIngo Weinhold
121a38a92c9SIngo Weinhold
122ada99c21SIngo Weinhold// #pragma mark - Command
123ada99c21SIngo Weinhold
124ada99c21SIngo WeinholdCommand::Command(const char* name, const char* description)
125ada99c21SIngo Weinhold	: fName(name),
126ada99c21SIngo Weinhold	  fDescription(description)
127ada99c21SIngo Weinhold{
128ada99c21SIngo Weinhold}
129ada99c21SIngo Weinhold
130ada99c21SIngo Weinhold
131ada99c21SIngo WeinholdCommand::Command(command_function* function, const char* name,
132ada99c21SIngo Weinhold	const char* description)
133ada99c21SIngo Weinhold	: fName(name),
134ada99c21SIngo Weinhold	  fDescription(description),
135ada99c21SIngo Weinhold	  fFunction(function)
136ada99c21SIngo Weinhold{
137ada99c21SIngo Weinhold}
138ada99c21SIngo Weinhold
139ada99c21SIngo Weinhold
140ada99c21SIngo WeinholdCommand::~Command()
141ada99c21SIngo Weinhold{
142ada99c21SIngo Weinhold}
143ada99c21SIngo Weinhold
144ada99c21SIngo Weinhold
145ada99c21SIngo Weinholdconst char*
146ada99c21SIngo WeinholdCommand::Name() const
147ada99c21SIngo Weinhold{
148ada99c21SIngo Weinhold	return fName.c_str();
149ada99c21SIngo Weinhold}
150ada99c21SIngo Weinhold
151ada99c21SIngo Weinhold
152ada99c21SIngo Weinholdconst char*
153ada99c21SIngo WeinholdCommand::Description() const
154ada99c21SIngo Weinhold{
155ada99c21SIngo Weinhold	return fDescription.c_str();
156ada99c21SIngo Weinhold}
157ada99c21SIngo Weinhold
158ada99c21SIngo Weinhold
1594f7504e3SIngo Weinholdfssh_status_t
160ada99c21SIngo WeinholdCommand::Do(int argc, const char* const* argv)
161ada99c21SIngo Weinhold{
162ada99c21SIngo Weinhold	if (!fFunction) {
163ada99c21SIngo Weinhold		fprintf(stderr, "No function given for command \"%s\"\n", Name());
1644f7504e3SIngo Weinhold		return FSSH_B_BAD_VALUE;
165ada99c21SIngo Weinhold	}
166ada99c21SIngo Weinhold
167ada99c21SIngo Weinhold	return (*fFunction)(argc, argv);
168ada99c21SIngo Weinhold}
169ada99c21SIngo Weinhold
170ada99c21SIngo Weinhold
171ada99c21SIngo Weinhold// #pragma mark - CommandManager
172ada99c21SIngo Weinhold
173ada99c21SIngo WeinholdCommandManager::CommandManager()
174ada99c21SIngo Weinhold{
175ada99c21SIngo Weinhold}
176ada99c21SIngo Weinhold
177ada99c21SIngo Weinhold
178ada99c21SIngo WeinholdCommandManager*
179ada99c21SIngo WeinholdCommandManager::Default()
180ada99c21SIngo Weinhold{
181ada99c21SIngo Weinhold	if (!sManager)
182ada99c21SIngo Weinhold		sManager = new CommandManager;
183ada99c21SIngo Weinhold	return sManager;
184ada99c21SIngo Weinhold}
185ada99c21SIngo Weinhold
186ada99c21SIngo Weinhold
187ada99c21SIngo Weinholdvoid
188ada99c21SIngo WeinholdCommandManager::AddCommand(Command* command)
189ada99c21SIngo Weinhold{
190ada99c21SIngo Weinhold	// The command name may consist of several aliases. Split them and
191ada99c21SIngo Weinhold	// register the command for each of them.
192ada99c21SIngo Weinhold	char _names[1024];
193ada99c21SIngo Weinhold	char* names = _names;
194ada99c21SIngo Weinhold	strcpy(names, command->Name());
195ada99c21SIngo Weinhold
196ada99c21SIngo Weinhold	char* cookie;
197ada99c21SIngo Weinhold	while (char* name = strtok_r(names, " /", &cookie)) {
198ada99c21SIngo Weinhold		fCommands[name] = command;
199ada99c21SIngo Weinhold		names = NULL;
200ada99c21SIngo Weinhold	}
201ada99c21SIngo Weinhold}
202ada99c21SIngo Weinhold
203ada99c21SIngo Weinhold
204ada99c21SIngo Weinholdvoid
205ada99c21SIngo WeinholdCommandManager::AddCommand(command_function* function, const char* name,
206ada99c21SIngo Weinhold	const char* description)
207ada99c21SIngo Weinhold{
208ada99c21SIngo Weinhold	AddCommand(new Command(function, name, description));
209ada99c21SIngo Weinhold}
210ada99c21SIngo Weinhold
211ada99c21SIngo Weinhold
212ada99c21SIngo Weinholdvoid
213ada99c21SIngo WeinholdCommandManager::AddCommands(command_function* function, const char* name,
214ada99c21SIngo Weinhold	const char* description, ...)
215ada99c21SIngo Weinhold{
216ada99c21SIngo Weinhold	va_list args;
217ada99c21SIngo Weinhold	va_start(args, description);
218ada99c21SIngo Weinhold
219ada99c21SIngo Weinhold	while (function) {
220ada99c21SIngo Weinhold		AddCommand(function, name, description);
221ada99c21SIngo Weinhold
222ada99c21SIngo Weinhold		function = va_arg(args, command_function*);
223ada99c21SIngo Weinhold		if (function) {
224ada99c21SIngo Weinhold			name = va_arg(args, const char*);
225ada99c21SIngo Weinhold			description = va_arg(args, const char*);
226ada99c21SIngo Weinhold		}
227ada99c21SIngo Weinhold	}
228ada99c21SIngo Weinhold
229ada99c21SIngo Weinhold	va_end(args);
230ada99c21SIngo Weinhold}
231ada99c21SIngo Weinhold
232ada99c21SIngo Weinhold
233ada99c21SIngo WeinholdCommand*
234ada99c21SIngo WeinholdCommandManager::FindCommand(const char* name) const
235ada99c21SIngo Weinhold{
236ada99c21SIngo Weinhold	CommandMap::const_iterator it = fCommands.find(name);
237ada99c21SIngo Weinhold	if (it == fCommands.end())
238ada99c21SIngo Weinhold		return NULL;
239ada99c21SIngo Weinhold
240ada99c21SIngo Weinhold	return it->second;
241ada99c21SIngo Weinhold}
242ada99c21SIngo Weinhold
243ada99c21SIngo Weinhold
244ada99c21SIngo Weinholdvoid
245ada99c21SIngo WeinholdCommandManager::ListCommands() const
246ada99c21SIngo Weinhold{
247ada99c21SIngo Weinhold	for (CommandMap::const_iterator it = fCommands.begin();
248ada99c21SIngo Weinhold			it != fCommands.end(); ++it) {
249ada99c21SIngo Weinhold		const char* name = it->first.c_str();
250ada99c21SIngo Weinhold		Command* command = it->second;
251ada99c21SIngo Weinhold		printf("%-16s - %s\n", name, command->Description());
252ada99c21SIngo Weinhold	}
253ada99c21SIngo Weinhold}
254ada99c21SIngo Weinhold
255ada99c21SIngo Weinhold
256ada99c21SIngo WeinholdCommandManager*	CommandManager::sManager = NULL;
257ada99c21SIngo Weinhold
258ada99c21SIngo Weinhold
259114d7934SAxel Dörfler// #pragma mark - Command support functions
260114d7934SAxel Dörfler
261114d7934SAxel Dörfler
262114d7934SAxel Dörflerstatic bool
263114d7934SAxel Dörflerget_permissions(const char* modeString, fssh_mode_t& _permissions)
264114d7934SAxel Dörfler{
265114d7934SAxel Dörfler	// currently only octal mode is supported
266114d7934SAxel Dörfler	if (strlen(modeString) != 3)
267114d7934SAxel Dörfler		return false;
268114d7934SAxel Dörfler
269114d7934SAxel Dörfler	fssh_mode_t permissions = 0;
270114d7934SAxel Dörfler	for (int i = 0; i < 3; i++) {
271114d7934SAxel Dörfler		char c = modeString[i];
272114d7934SAxel Dörfler		if (c < '0' || c > '7')
273114d7934SAxel Dörfler			return false;
274114d7934SAxel Dörfler		permissions = (permissions << 3) | (c - '0');
275114d7934SAxel Dörfler	}
276114d7934SAxel Dörfler
277114d7934SAxel Dörfler	_permissions = permissions;
278114d7934SAxel Dörfler	return true;
279114d7934SAxel Dörfler}
280114d7934SAxel Dörfler
281114d7934SAxel Dörfler
282114d7934SAxel Dörflerstatic fssh_dev_t
283114d7934SAxel Dörflerget_volume_id()
284114d7934SAxel Dörfler{
285114d7934SAxel Dörfler	struct fssh_stat st;
286114d7934SAxel Dörfler	fssh_status_t error = _kern_read_stat(-1, kMountPoint, false, &st,
287114d7934SAxel Dörfler		sizeof(st));
288114d7934SAxel Dörfler	if (error != FSSH_B_OK) {
289114d7934SAxel Dörfler		fprintf(stderr, "Error: Failed to stat() mount point: %s\n",
290114d7934SAxel Dörfler			fssh_strerror(error));
291114d7934SAxel Dörfler		return error;
292114d7934SAxel Dörfler	}
293114d7934SAxel Dörfler
294114d7934SAxel Dörfler	return st.fssh_st_dev;
295114d7934SAxel Dörfler}
296114d7934SAxel Dörfler
297114d7934SAxel Dörfler
298114d7934SAxel Dörflerstatic const char *
299114d7934SAxel Dörflerbyte_string(int64_t numBlocks, int64_t blockSize)
300114d7934SAxel Dörfler{
301114d7934SAxel Dörfler	double blocks = 1. * numBlocks * blockSize;
302114d7934SAxel Dörfler	static char string[64];
303114d7934SAxel Dörfler
304114d7934SAxel Dörfler	if (blocks < 1024)
305cf844822SIngo Weinhold		sprintf(string, "%" FSSH_B_PRId64, numBlocks * blockSize);
306114d7934SAxel Dörfler	else {
307114d7934SAxel Dörfler		const char* units[] = {"K", "M", "G", NULL};
308114d7934SAxel Dörfler		int i = -1;
309114d7934SAxel Dörfler
310114d7934SAxel Dörfler		do {
311114d7934SAxel Dörfler			blocks /= 1024.0;
312114d7934SAxel Dörfler			i++;
313114d7934SAxel Dörfler		} while (blocks >= 1024 && units[i + 1]);
314114d7934SAxel Dörfler
315114d7934SAxel Dörfler		sprintf(string, "%.1f%s", blocks, units[i]);
316114d7934SAxel Dörfler	}
317114d7934SAxel Dörfler
318114d7934SAxel Dörfler	return string;
319114d7934SAxel Dörfler}
320114d7934SAxel Dörfler
321114d7934SAxel Dörfler
322114d7934SAxel Dörflervoid
323114d7934SAxel Dörflerprint_flag(uint32_t deviceFlags, uint32_t testFlag, const char *yes,
324114d7934SAxel Dörfler	const char *no)
325114d7934SAxel Dörfler{
3260714ea07SAxel Dörfler	printf("%s", (deviceFlags & testFlag) != 0 ? yes : no);
327114d7934SAxel Dörfler}
328114d7934SAxel Dörfler
329114d7934SAxel Dörfler
330114d7934SAxel Dörflerstatic void
331114d7934SAxel Dörflerlist_entry(const char* file, const char* name = NULL)
332114d7934SAxel Dörfler{
333114d7934SAxel Dörfler	// construct path, if a leaf name is given
334114d7934SAxel Dörfler	std::string path;
335114d7934SAxel Dörfler	if (name) {
336114d7934SAxel Dörfler		path = file;
337114d7934SAxel Dörfler		path += '/';
338114d7934SAxel Dörfler		path += name;
339114d7934SAxel Dörfler		file = path.c_str();
340114d7934SAxel Dörfler	} else
341114d7934SAxel Dörfler		name = file;
342114d7934SAxel Dörfler
343114d7934SAxel Dörfler	// stat the file
344114d7934SAxel Dörfler	struct fssh_stat st;
345114d7934SAxel Dörfler	fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st));
346114d7934SAxel Dörfler	if (error != FSSH_B_OK) {
347114d7934SAxel Dörfler		fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file,
348114d7934SAxel Dörfler			fssh_strerror(error));
349114d7934SAxel Dörfler		return;
350114d7934SAxel Dörfler	}
351114d7934SAxel Dörfler
352114d7934SAxel Dörfler	// get time
353114d7934SAxel Dörfler	struct tm time;
354114d7934SAxel Dörfler	time_t fileTime = st.fssh_st_mtime;
355114d7934SAxel Dörfler	localtime_r(&fileTime, &time);
356114d7934SAxel Dörfler
357114d7934SAxel Dörfler	// get permissions
358114d7934SAxel Dörfler	std::string permissions;
359114d7934SAxel Dörfler	fssh_mode_t mode = st.fssh_st_mode;
360114d7934SAxel Dörfler	// user
361114d7934SAxel Dörfler	permissions += ((mode & FSSH_S_IRUSR) ? 'r' : '-');
362114d7934SAxel Dörfler	permissions += ((mode & FSSH_S_IWUSR) ? 'w' : '-');
363114d7934SAxel Dörfler	if (mode & FSSH_S_ISUID)
364114d7934SAxel Dörfler		permissions += 's';
365114d7934SAxel Dörfler	else
366114d7934SAxel Dörfler		permissions += ((mode & FSSH_S_IXUSR) ? 'x' : '-');
367114d7934SAxel Dörfler	// group
368114d7934SAxel Dörfler	permissions += ((mode & FSSH_S_IRGRP) ? 'r' : '-');
369114d7934SAxel Dörfler	permissions += ((mode & FSSH_S_IWGRP) ? 'w' : '-');
370114d7934SAxel Dörfler	if (mode & FSSH_S_ISGID)
371114d7934SAxel Dörfler		permissions += 's';
372114d7934SAxel Dörfler	else
373114d7934SAxel Dörfler		permissions += ((mode & FSSH_S_IXGRP) ? 'x' : '-');
374114d7934SAxel Dörfler	// others
375114d7934SAxel Dörfler	permissions += ((mode & FSSH_S_IROTH) ? 'r' : '-');
376114d7934SAxel Dörfler	permissions += ((mode & FSSH_S_IWOTH) ? 'w' : '-');
377114d7934SAxel Dörfler	permissions += ((mode & FSSH_S_IXOTH) ? 'x' : '-');
378114d7934SAxel Dörfler
379114d7934SAxel Dörfler	// get file type
380114d7934SAxel Dörfler	char fileType = '?';
381114d7934SAxel Dörfler	if (FSSH_S_ISREG(mode)) {
382114d7934SAxel Dörfler		fileType = '-';
383114d7934SAxel Dörfler	} else if (FSSH_S_ISLNK(mode)) {
384114d7934SAxel Dörfler		fileType = 'l';
385114d7934SAxel Dörfler	} else if (FSSH_S_ISBLK(mode)) {
386114d7934SAxel Dörfler		fileType = 'b';
387114d7934SAxel Dörfler	} else if (FSSH_S_ISDIR(mode)) {
388114d7934SAxel Dörfler		fileType = 'd';
389114d7934SAxel Dörfler	} else if (FSSH_S_ISCHR(mode)) {
390114d7934SAxel Dörfler		fileType = 'c';
391114d7934SAxel Dörfler	} else if (FSSH_S_ISFIFO(mode)) {
392114d7934SAxel Dörfler		fileType = 'f';
393114d7934SAxel Dörfler	} else if (FSSH_S_ISINDEX(mode)) {
394114d7934SAxel Dörfler		fileType = 'i';
395114d7934SAxel Dörfler	}
396114d7934SAxel Dörfler
397114d7934SAxel Dörfler	// get link target
398114d7934SAxel Dörfler	std::string nameSuffix;
399114d7934SAxel Dörfler	if (FSSH_S_ISLNK(mode)) {
400114d7934SAxel Dörfler		char buffer[FSSH_B_PATH_NAME_LENGTH];
401114d7934SAxel Dörfler		fssh_size_t size = sizeof(buffer) - 1;
402114d7934SAxel Dörfler		error = _kern_read_link(-1, file, buffer, &size);
403114d7934SAxel Dörfler		if (error != FSSH_B_OK)
404114d7934SAxel Dörfler			snprintf(buffer, sizeof(buffer), "(%s)", fssh_strerror(error));
405114d7934SAxel Dörfler
406114d7934SAxel Dörfler		buffer[size] = '\0';
407114d7934SAxel Dörfler		nameSuffix += " -> ";
408114d7934SAxel Dörfler		nameSuffix += buffer;
409114d7934SAxel Dörfler	}
410114d7934SAxel Dörfler
411cf844822SIngo Weinhold	printf("%c%s %2d %2d %10" FSSH_B_PRIdOFF
412cf844822SIngo Weinhold		" %d-%02d-%02d %02d:%02d:%02d %s%s\n",
413114d7934SAxel Dörfler		fileType, permissions.c_str(), (int)st.fssh_st_uid, (int)st.fssh_st_gid,
414114d7934SAxel Dörfler		st.fssh_st_size,
415114d7934SAxel Dörfler		1900 + time.tm_year, 1 + time.tm_mon, time.tm_mday,
416114d7934SAxel Dörfler		time.tm_hour, time.tm_min, time.tm_sec,
417114d7934SAxel Dörfler		name, nameSuffix.c_str());
418114d7934SAxel Dörfler}
419114d7934SAxel Dörfler
420114d7934SAxel Dörfler
421114d7934SAxel Dörflerstatic fssh_status_t
422114d7934SAxel Dörflercreate_dir(const char *path, bool createParents)
423114d7934SAxel Dörfler{
424114d7934SAxel Dörfler	// stat the entry
425114d7934SAxel Dörfler	struct fssh_stat st;
426114d7934SAxel Dörfler	fssh_status_t error = _kern_read_stat(-1, path, false, &st, sizeof(st));
427114d7934SAxel Dörfler	if (error == FSSH_B_OK) {
428114d7934SAxel Dörfler		if (createParents && FSSH_S_ISDIR(st.fssh_st_mode))
429114d7934SAxel Dörfler			return FSSH_B_OK;
430114d7934SAxel Dörfler
431114d7934SAxel Dörfler		fprintf(stderr, "Error: Cannot make dir, entry \"%s\" is in the way.\n",
432114d7934SAxel Dörfler			path);
433114d7934SAxel Dörfler		return FSSH_B_FILE_EXISTS;
434114d7934SAxel Dörfler	}
435114d7934SAxel Dörfler
436114d7934SAxel Dörfler	// the dir doesn't exist yet
437114d7934SAxel Dörfler	// if we shall create all parents, do that first
438114d7934SAxel Dörfler	if (createParents) {
439114d7934SAxel Dörfler		// create the parent dir path
440114d7934SAxel Dörfler		// eat the trailing '/'s
441114d7934SAxel Dörfler		int len = strlen(path);
442114d7934SAxel Dörfler		while (len > 0 && path[len - 1] == '/')
443114d7934SAxel Dörfler			len--;
444114d7934SAxel Dörfler
445114d7934SAxel Dörfler		// eat the last path component
446114d7934SAxel Dörfler		while (len > 0 && path[len - 1] != '/')
447114d7934SAxel Dörfler			len--;
448114d7934SAxel Dörfler
449114d7934SAxel Dörfler		// eat the trailing '/'s
450114d7934SAxel Dörfler		while (len > 0 && path[len - 1] == '/')
451114d7934SAxel Dörfler			len--;
452114d7934SAxel Dörfler
453114d7934SAxel Dörfler		// Now either nothing remains, which means we had a single component,
454114d7934SAxel Dörfler		// a root subdir -- in those cases we can just fall through (we should
455114d7934SAxel Dörfler		// actually never be here in case of the root dir, but anyway) -- or
456114d7934SAxel Dörfler		// there is something left, which we can call a parent directory and
457114d7934SAxel Dörfler		// try to create it.
458114d7934SAxel Dörfler		if (len > 0) {
459114d7934SAxel Dörfler			char *parentPath = (char*)malloc(len + 1);
460114d7934SAxel Dörfler			if (!parentPath) {
461114d7934SAxel Dörfler				fprintf(stderr, "Error: Failed to allocate memory for parent "
462114d7934SAxel Dörfler					"path.\n");
463114d7934SAxel Dörfler				return FSSH_B_NO_MEMORY;
464114d7934SAxel Dörfler			}
465114d7934SAxel Dörfler			memcpy(parentPath, path, len);
466114d7934SAxel Dörfler			parentPath[len] = '\0';
467114d7934SAxel Dörfler
468114d7934SAxel Dörfler			error = create_dir(parentPath, createParents);
469114d7934SAxel Dörfler
470114d7934SAxel Dörfler			free(parentPath);
471114d7934SAxel Dörfler
472114d7934SAxel Dörfler			if (error != FSSH_B_OK)
473114d7934SAxel Dörfler				return error;
474114d7934SAxel Dörfler		}
475114d7934SAxel Dörfler	}
476114d7934SAxel Dörfler
477114d7934SAxel Dörfler	// make the directory
478114d7934SAxel Dörfler	error = _kern_create_dir(-1,
479114d7934SAxel Dörfler		path, (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
480114d7934SAxel Dörfler	if (error != FSSH_B_OK) {
481114d7934SAxel Dörfler		fprintf(stderr, "Error: Failed to make directory \"%s\": %s\n", path,
482114d7934SAxel Dörfler			fssh_strerror(error));
483114d7934SAxel Dörfler		return error;
484114d7934SAxel Dörfler	}
485114d7934SAxel Dörfler
486114d7934SAxel Dörfler	return FSSH_B_OK;
487114d7934SAxel Dörfler}
488114d7934SAxel Dörfler
489114d7934SAxel Dörfler
490114d7934SAxel Dörflerstatic fssh_status_t remove_entry(int dir, const char *entry, bool recursive,
491114d7934SAxel Dörfler	bool force);
492114d7934SAxel Dörfler
493114d7934SAxel Dörfler
494114d7934SAxel Dörflerstatic fssh_status_t
495114d7934SAxel Dörflerremove_dir_contents(int parentDir, const char *name, bool force)
496114d7934SAxel Dörfler{
497114d7934SAxel Dörfler	// open the dir
498114d7934SAxel Dörfler	int dir = _kern_open_dir(parentDir, name);
499114d7934SAxel Dörfler	if (dir < 0) {
500114d7934SAxel Dörfler		fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", name,
501114d7934SAxel Dörfler			fssh_strerror(dir));
502114d7934SAxel Dörfler		return dir;
503114d7934SAxel Dörfler	}
504114d7934SAxel Dörfler
505114d7934SAxel Dörfler	fssh_status_t error = FSSH_B_OK;
506114d7934SAxel Dörfler
507114d7934SAxel Dörfler	// iterate through the entries
508114d7934SAxel Dörfler	fssh_ssize_t numRead;
509114d7934SAxel Dörfler	char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
510114d7934SAxel Dörfler	fssh_dirent *entry = (fssh_dirent*)buffer;
511114d7934SAxel Dörfler	while ((numRead = _kern_read_dir(dir, entry, sizeof(buffer), 1)) > 0) {
512114d7934SAxel Dörfler		// skip "." and ".."
513114d7934SAxel Dörfler		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
514114d7934SAxel Dörfler			continue;
515114d7934SAxel Dörfler
516114d7934SAxel Dörfler		error = remove_entry(dir, entry->d_name, true, force);
517114d7934SAxel Dörfler		if (error != FSSH_B_OK)
518114d7934SAxel Dörfler			break;
519114d7934SAxel Dörfler	}
520114d7934SAxel Dörfler
521114d7934SAxel Dörfler	if (numRead < 0) {
522114d7934SAxel Dörfler		fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n", name,
523114d7934SAxel Dörfler			fssh_strerror(numRead));
524114d7934SAxel Dörfler		error = numRead;
525114d7934SAxel Dörfler	}
526114d7934SAxel Dörfler
527114d7934SAxel Dörfler	// close
528114d7934SAxel Dörfler	_kern_close(dir);
529114d7934SAxel Dörfler
530114d7934SAxel Dörfler	return error;
531114d7934SAxel Dörfler}
532114d7934SAxel Dörfler
533114d7934SAxel Dörfler
534114d7934SAxel Dörflerstatic fssh_status_t
535114d7934SAxel Dörflerremove_entry(int dir, const char *entry, bool recursive, bool force)
536114d7934SAxel Dörfler{
537114d7934SAxel Dörfler	// stat the file
538114d7934SAxel Dörfler	struct fssh_stat st;
539114d7934SAxel Dörfler	fssh_status_t error = _kern_read_stat(dir, entry, false, &st, sizeof(st));
540114d7934SAxel Dörfler	if (error != FSSH_B_OK) {
541114d7934SAxel Dörfler		if (force && error == FSSH_B_ENTRY_NOT_FOUND)
542114d7934SAxel Dörfler			return FSSH_B_OK;
543114d7934SAxel Dörfler
544114d7934SAxel Dörfler		fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", entry,
545114d7934SAxel Dörfler			fssh_strerror(error));
546114d7934SAxel Dörfler		return error;
547114d7934SAxel Dörfler	}
548114d7934SAxel Dörfler
549114d7934SAxel Dörfler	if (FSSH_S_ISDIR(st.fssh_st_mode)) {
550114d7934SAxel Dörfler		if (!recursive) {
551114d7934SAxel Dörfler			fprintf(stderr, "Error: \"%s\" is a directory.\n", entry);
552114d7934SAxel Dörfler				// TODO: get the full path
553114d7934SAxel Dörfler			return FSSH_EISDIR;
554114d7934SAxel Dörfler		}
555114d7934SAxel Dörfler
556114d7934SAxel Dörfler		// remove the contents
557114d7934SAxel Dörfler		error = remove_dir_contents(dir, entry, force);
558114d7934SAxel Dörfler		if (error != FSSH_B_OK)
559114d7934SAxel Dörfler			return error;
560114d7934SAxel Dörfler
561114d7934SAxel Dörfler		// remove the directory
562114d7934SAxel Dörfler		error = _kern_remove_dir(dir, entry);
563114d7934SAxel Dörfler		if (error != FSSH_B_OK) {
564114d7934SAxel Dörfler			fprintf(stderr, "Error: Failed to remove directory \"%s\": %s\n",
565114d7934SAxel Dörfler				entry, fssh_strerror(error));
566114d7934SAxel Dörfler			return error;
567114d7934SAxel Dörfler		}
568114d7934SAxel Dörfler	} else {
569114d7934SAxel Dörfler		// remove the entry
570114d7934SAxel Dörfler		error = _kern_unlink(dir, entry);
571114d7934SAxel Dörfler		if (error != FSSH_B_OK) {
572114d7934SAxel Dörfler			fprintf(stderr, "Error: Failed to remove entry \"%s\": %s\n", entry,
573114d7934SAxel Dörfler				fssh_strerror(error));
574114d7934SAxel Dörfler			return error;
575114d7934SAxel Dörfler		}
576114d7934SAxel Dörfler	}
577114d7934SAxel Dörfler
578114d7934SAxel Dörfler	return FSSH_B_OK;
579114d7934SAxel Dörfler}
580114d7934SAxel Dörfler
581114d7934SAxel Dörfler
5820714ea07SAxel Dörflerstatic fssh_status_t
5830714ea07SAxel Dörflermove_entry(int dir, const char *entry, int targetDir, const char* target,
5840714ea07SAxel Dörfler	bool force)
5850714ea07SAxel Dörfler{
5860714ea07SAxel Dörfler	// stat the file
5870714ea07SAxel Dörfler	struct fssh_stat st;
5880714ea07SAxel Dörfler	fssh_status_t status = _kern_read_stat(dir, entry, false, &st, sizeof(st));
5890714ea07SAxel Dörfler	if (status != FSSH_B_OK) {
5900714ea07SAxel Dörfler		if (force && status == FSSH_B_ENTRY_NOT_FOUND)
5910714ea07SAxel Dörfler			return FSSH_B_OK;
5920714ea07SAxel Dörfler
5930714ea07SAxel Dörfler		fprintf(stderr, "Error: Failed to move \"%s\": %s\n", entry,
5940714ea07SAxel Dörfler			fssh_strerror(status));
5950714ea07SAxel Dörfler		return status;
5960714ea07SAxel Dörfler	}
5970714ea07SAxel Dörfler
5980714ea07SAxel Dörfler	return _kern_rename(dir, entry, targetDir, target);
5990714ea07SAxel Dörfler}
6000714ea07SAxel Dörfler
6010714ea07SAxel Dörfler
602ada99c21SIngo Weinhold// #pragma mark - Commands
603ada99c21SIngo Weinhold
604ada99c21SIngo Weinhold
6054f7504e3SIngo Weinholdstatic fssh_status_t
606ada99c21SIngo Weinholdcommand_cd(int argc, const char* const* argv)
607ada99c21SIngo Weinhold{
608ada99c21SIngo Weinhold	if (argc != 2) {
6094f7504e3SIngo Weinhold		fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
6104f7504e3SIngo Weinhold		return FSSH_B_BAD_VALUE;
611ada99c21SIngo Weinhold	}
612ada99c21SIngo Weinhold	const char* directory = argv[1];
613ada99c21SIngo Weinhold
6142842773aSIngo Weinhold	fssh_status_t error = FSSH_B_OK;
6152842773aSIngo Weinhold	if (directory[0] == ':') {
6162842773aSIngo Weinhold		if (chdir(directory + 1) < 0)
6172842773aSIngo Weinhold			error = fssh_get_errno();
6182842773aSIngo Weinhold	} else
6192842773aSIngo Weinhold		error = _kern_setcwd(-1, directory);
6202842773aSIngo Weinhold
621ada99c21SIngo Weinhold	if (error != FSSH_B_OK) {
622ada99c21SIngo Weinhold		fprintf(stderr, "Error: cd %s: %s\n", directory, fssh_strerror(error));
6234f7504e3SIngo Weinhold		return error;
624ada99c21SIngo Weinhold	}
625ada99c21SIngo Weinhold
6264f7504e3SIngo Weinhold	return FSSH_B_OK;
627ada99c21SIngo Weinhold}
628ada99c21SIngo Weinhold
629ada99c21SIngo Weinhold
63045eb999eSIngo Weinholdstatic fssh_status_t
63145eb999eSIngo Weinholdcommand_chmod(int argc, const char* const* argv)
63245eb999eSIngo Weinhold{
63345eb999eSIngo Weinhold	bool recursive = false;
63445eb999eSIngo Weinhold
63545eb999eSIngo Weinhold	// parse parameters
63645eb999eSIngo Weinhold	int argi = 1;
63745eb999eSIngo Weinhold	for (argi = 1; argi < argc; argi++) {
63845eb999eSIngo Weinhold		const char *arg = argv[argi];
63945eb999eSIngo Weinhold		if (arg[0] != '-')
64045eb999eSIngo Weinhold			break;
64145eb999eSIngo Weinhold
64245eb999eSIngo Weinhold		if (arg[1] == '\0') {
64345eb999eSIngo Weinhold			fprintf(stderr, "Error: Invalid option \"-\"\n");
64445eb999eSIngo Weinhold			return FSSH_B_BAD_VALUE;
64545eb999eSIngo Weinhold		}
64645eb999eSIngo Weinhold
64745eb999eSIngo Weinhold		for (int i = 1; arg[i]; i++) {
64845eb999eSIngo Weinhold			switch (arg[i]) {
64945eb999eSIngo Weinhold				case 'R':
65045eb999eSIngo Weinhold					recursive = true;
65145eb999eSIngo Weinhold					fprintf(stderr, "Sorry, recursive mode not supported "
65245eb999eSIngo Weinhold						"yet.\n");
65345eb999eSIngo Weinhold					return FSSH_B_BAD_VALUE;
65445eb999eSIngo Weinhold				default:
65545eb999eSIngo Weinhold					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
65645eb999eSIngo Weinhold					return FSSH_B_BAD_VALUE;
65745eb999eSIngo Weinhold			}
65845eb999eSIngo Weinhold		}
65945eb999eSIngo Weinhold	}
66045eb999eSIngo Weinhold
66145eb999eSIngo Weinhold	// get mode
66245eb999eSIngo Weinhold	fssh_mode_t permissions;
66345eb999eSIngo Weinhold	if (argi + 1 >= argc || !get_permissions(argv[argi++], permissions)) {
66445eb999eSIngo Weinhold		printf("Usage: %s [ -R ] <octal mode> <file>...\n", argv[0]);
66545eb999eSIngo Weinhold		return FSSH_B_BAD_VALUE;
66645eb999eSIngo Weinhold	}
66745eb999eSIngo Weinhold
66845eb999eSIngo Weinhold	fssh_struct_stat st;
66945eb999eSIngo Weinhold	st.fssh_st_mode = permissions;
67045eb999eSIngo Weinhold
67145eb999eSIngo Weinhold	// chmod loop
67245eb999eSIngo Weinhold	for (; argi < argc; argi++) {
67345eb999eSIngo Weinhold		const char *file = argv[argi];
67445eb999eSIngo Weinhold		if (strlen(file) == 0) {
67545eb999eSIngo Weinhold			fprintf(stderr, "Error: An empty path is not a valid argument!\n");
67645eb999eSIngo Weinhold			return FSSH_B_BAD_VALUE;
67745eb999eSIngo Weinhold		}
67845eb999eSIngo Weinhold
67945eb999eSIngo Weinhold		fssh_status_t error = _kern_write_stat(-1, file, false, &st, sizeof(st),
6805e2ef462SAxel Dörfler			FSSH_B_STAT_MODE);
68145eb999eSIngo Weinhold		if (error != FSSH_B_OK) {
68245eb999eSIngo Weinhold			fprintf(stderr, "Error: Failed to change mode of \"%s\"!\n", file);
68345eb999eSIngo Weinhold			return error;
68445eb999eSIngo Weinhold		}
68545eb999eSIngo Weinhold	}
68645eb999eSIngo Weinhold
68745eb999eSIngo Weinhold	return FSSH_B_OK;
68845eb999eSIngo Weinhold}
68945eb999eSIngo Weinhold
69045eb999eSIngo Weinhold
6917f46de0aSAugustin Cavalierstatic fssh_status_t
6927f46de0aSAugustin Cavaliercommand_cat(int argc, const char* const* argv)
6937f46de0aSAugustin Cavalier{
6947f46de0aSAugustin Cavalier	size_t numBytes = 10;
6957f46de0aSAugustin Cavalier	int fileStart = 1;
6967f46de0aSAugustin Cavalier	if (argc < 2 || strcmp(argv[1], "--help") == 0) {
6977f46de0aSAugustin Cavalier		printf("Usage: %s [ -n ] [FILE]...\n"
6987f46de0aSAugustin Cavalier			"\t -n\tNumber of bytes to read\n",
6997f46de0aSAugustin Cavalier			argv[0]);
7007f46de0aSAugustin Cavalier		return FSSH_B_OK;
7017f46de0aSAugustin Cavalier	}
7027f46de0aSAugustin Cavalier
7037f46de0aSAugustin Cavalier	if (argc > 3 && strcmp(argv[1], "-n") == 0) {
7047f46de0aSAugustin Cavalier		fileStart += 2;
7057f46de0aSAugustin Cavalier		numBytes = strtol(argv[2], NULL, 10);
7067f46de0aSAugustin Cavalier	}
7077f46de0aSAugustin Cavalier
7087f46de0aSAugustin Cavalier	const char* const* files = argv + fileStart;
7097f46de0aSAugustin Cavalier	for (; *files; files++) {
7107f46de0aSAugustin Cavalier		const char* file = *files;
7117f46de0aSAugustin Cavalier		int fd = _kern_open(-1, file, FSSH_O_RDONLY, FSSH_O_RDONLY);
7127f46de0aSAugustin Cavalier		if (fd < 0) {
7137f46de0aSAugustin Cavalier			fprintf(stderr, "error: %s\n", fssh_strerror(fd));
7147f46de0aSAugustin Cavalier			return FSSH_B_BAD_VALUE;
7157f46de0aSAugustin Cavalier		}
7167f46de0aSAugustin Cavalier
7177f46de0aSAugustin Cavalier		char buffer[numBytes + 1];
7187f46de0aSAugustin Cavalier		if (buffer == NULL) {
7197f46de0aSAugustin Cavalier			fprintf(stderr, "error: No memory\n");
7207f46de0aSAugustin Cavalier			_kern_close(fd);
7217f46de0aSAugustin Cavalier			return FSSH_B_NO_MEMORY;
7227f46de0aSAugustin Cavalier		}
7237f46de0aSAugustin Cavalier
7247f46de0aSAugustin Cavalier		if (_kern_read(fd, 0, buffer, numBytes) != (ssize_t)numBytes) {
7257f46de0aSAugustin Cavalier			fprintf(stderr, "error reading: %s\n", fssh_strerror(fd));
7267f46de0aSAugustin Cavalier			_kern_close(fd);
7277f46de0aSAugustin Cavalier			return FSSH_B_BAD_VALUE;
7287f46de0aSAugustin Cavalier		}
7297f46de0aSAugustin Cavalier
7307f46de0aSAugustin Cavalier		_kern_close(fd);
7317f46de0aSAugustin Cavalier		buffer[numBytes] = '\0';
7327f46de0aSAugustin Cavalier		printf("%s\n", buffer);
7337f46de0aSAugustin Cavalier	}
7347f46de0aSAugustin Cavalier
7357f46de0aSAugustin Cavalier	return FSSH_B_OK;
7367f46de0aSAugustin Cavalier}
7377f46de0aSAugustin Cavalier
7387f46de0aSAugustin Cavalier
7394f7504e3SIngo Weinholdstatic fssh_status_t
740ada99c21SIngo Weinholdcommand_help(int argc, const char* const* argv)
741ada99c21SIngo Weinhold{
742ada99c21SIngo Weinhold	printf("supported commands:\n");
743ada99c21SIngo Weinhold	CommandManager::Default()->ListCommands();
7444f7504e3SIngo Weinhold	return FSSH_B_OK;
7454f7504e3SIngo Weinhold}
7464f7504e3SIngo Weinhold
7474f7504e3SIngo Weinhold
748114d7934SAxel Dörflerstatic fssh_status_t
749114d7934SAxel Dörflercommand_info(int argc, const char* const* argv)
750114d7934SAxel Dörfler{
751114d7934SAxel Dörfler	fssh_dev_t volumeID = get_volume_id();
752114d7934SAxel Dörfler	if (volumeID < 0)
753114d7934SAxel Dörfler		return volumeID;
754114d7934SAxel Dörfler
755114d7934SAxel Dörfler	fssh_fs_info info;
756114d7934SAxel Dörfler	fssh_status_t status = _kern_read_fs_info(volumeID, &info);
757114d7934SAxel Dörfler	if (status != FSSH_B_OK)
758114d7934SAxel Dörfler		return status;
759114d7934SAxel Dörfler
760cf844822SIngo Weinhold	printf("root inode:   %" FSSH_B_PRIdINO "\n", info.root);
761114d7934SAxel Dörfler	printf("flags:        ");
762114d7934SAxel Dörfler	print_flag(info.flags, FSSH_B_FS_HAS_QUERY, "Q", "-");
763114d7934SAxel Dörfler	print_flag(info.flags, FSSH_B_FS_HAS_ATTR, "A", "-");
764114d7934SAxel Dörfler	print_flag(info.flags, FSSH_B_FS_HAS_MIME, "M", "-");
765114d7934SAxel Dörfler	print_flag(info.flags, FSSH_B_FS_IS_SHARED, "S", "-");
766114d7934SAxel Dörfler	print_flag(info.flags, FSSH_B_FS_IS_PERSISTENT, "P", "-");
767114d7934SAxel Dörfler	print_flag(info.flags, FSSH_B_FS_IS_REMOVABLE, "R", "-");
768114d7934SAxel Dörfler	print_flag(info.flags, FSSH_B_FS_IS_READONLY, "-", "W");
769114d7934SAxel Dörfler
770cf844822SIngo Weinhold	printf("\nblock size:   %" FSSH_B_PRIdOFF "\n", info.block_size);
771cf844822SIngo Weinhold	printf("I/O size:     %" FSSH_B_PRIdOFF "\n", info.io_size);
772cf844822SIngo Weinhold	printf("total size:   %s (%" FSSH_B_PRIdOFF " blocks)\n",
773114d7934SAxel Dörfler		byte_string(info.total_blocks, info.block_size), info.total_blocks);
774cf844822SIngo Weinhold	printf("free size:    %s (%" FSSH_B_PRIdOFF " blocks)\n",
775114d7934SAxel Dörfler		byte_string(info.free_blocks, info.block_size), info.free_blocks);
776cf844822SIngo Weinhold	printf("total nodes:  %" FSSH_B_PRIdOFF "\n", info.total_nodes);
777cf844822SIngo Weinhold	printf("free nodes:   %" FSSH_B_PRIdOFF "\n", info.free_nodes);
778114d7934SAxel Dörfler	printf("volume name:  %s\n", info.volume_name);
779114d7934SAxel Dörfler	printf("fs name:      %s\n", info.fsh_name);
780114d7934SAxel Dörfler
781114d7934SAxel Dörfler	return FSSH_B_OK;
782114d7934SAxel Dörfler}
783114d7934SAxel Dörfler
784114d7934SAxel Dörfler
7854f7504e3SIngo Weinholdstatic fssh_status_t
7864f7504e3SIngo Weinholdcommand_ln(int argc, const char* const* argv)
7874f7504e3SIngo Weinhold{
7884f7504e3SIngo Weinhold	bool force = false;
7894f7504e3SIngo Weinhold	bool symbolic = false;
7904f7504e3SIngo Weinhold	bool dereference = true;
7914f7504e3SIngo Weinhold
7924f7504e3SIngo Weinhold	// parse parameters
7934f7504e3SIngo Weinhold	int argi = 1;
7944f7504e3SIngo Weinhold	for (argi = 1; argi < argc; argi++) {
7954f7504e3SIngo Weinhold		const char *arg = argv[argi];
7964f7504e3SIngo Weinhold		if (arg[0] != '-')
7974f7504e3SIngo Weinhold			break;
7984f7504e3SIngo Weinhold
7994f7504e3SIngo Weinhold		if (arg[1] == '\0') {
8004f7504e3SIngo Weinhold			fprintf(stderr, "Error: Invalid option \"-\"\n");
8014f7504e3SIngo Weinhold			return FSSH_B_BAD_VALUE;
8024f7504e3SIngo Weinhold		}
8034f7504e3SIngo Weinhold
8044f7504e3SIngo Weinhold		for (int i = 1; arg[i]; i++) {
8054f7504e3SIngo Weinhold			switch (arg[i]) {
8064f7504e3SIngo Weinhold				case 'f':
8074f7504e3SIngo Weinhold					force = true;
8084f7504e3SIngo Weinhold					break;
8094f7504e3SIngo Weinhold				case 's':
8104f7504e3SIngo Weinhold					symbolic = true;
8114f7504e3SIngo Weinhold					break;
8124f7504e3SIngo Weinhold				case 'n':
8134f7504e3SIngo Weinhold					dereference = false;
8144f7504e3SIngo Weinhold					break;
8154f7504e3SIngo Weinhold				default:
8164f7504e3SIngo Weinhold					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
8174f7504e3SIngo Weinhold					return FSSH_B_BAD_VALUE;
8184f7504e3SIngo Weinhold			}
8194f7504e3SIngo Weinhold		}
8204f7504e3SIngo Weinhold	}
8214f7504e3SIngo Weinhold
8224f7504e3SIngo Weinhold	if (argc - argi != 2) {
8234f7504e3SIngo Weinhold		fprintf(stderr, "Usage: %s [Options] <source> <target>\n", argv[0]);
8244f7504e3SIngo Weinhold		return FSSH_B_BAD_VALUE;
8254f7504e3SIngo Weinhold	}
8264f7504e3SIngo Weinhold
8274f7504e3SIngo Weinhold	const char *source = argv[argi];
8284f7504e3SIngo Weinhold	const char *target = argv[argi + 1];
8294f7504e3SIngo Weinhold
8304f7504e3SIngo Weinhold	// check, if the the target is an existing directory
8314f7504e3SIngo Weinhold	struct fssh_stat st;
8324f7504e3SIngo Weinhold	char targetBuffer[FSSH_B_PATH_NAME_LENGTH];
8334f7504e3SIngo Weinhold	fssh_status_t error = _kern_read_stat(-1, target, dereference, &st,
8344f7504e3SIngo Weinhold		sizeof(st));
8354f7504e3SIngo Weinhold	if (error == FSSH_B_OK) {
8364f7504e3SIngo Weinhold		if (FSSH_S_ISDIR(st.fssh_st_mode)) {
8374f7504e3SIngo Weinhold			// get source leaf
8384f7504e3SIngo Weinhold			char leaf[FSSH_B_FILE_NAME_LENGTH];
8394f7504e3SIngo Weinhold			error = get_last_path_component(source, leaf, sizeof(leaf));
840114d7934SAxel Dörfler			if (error != FSSH_B_OK) {
841114d7934SAxel Dörfler				fprintf(stderr, "Error: Failed to get leaf name of source "
842114d7934SAxel Dörfler					"path: %s\n", fssh_strerror(error));
843114d7934SAxel Dörfler				return error;
844114d7934SAxel Dörfler			}
845ada99c21SIngo Weinhold
846114d7934SAxel Dörfler			// compose a new path
847114d7934SAxel Dörfler			int len = strlen(target) + 1 + strlen(leaf);
848114d7934SAxel Dörfler			if (len > (int)sizeof(targetBuffer)) {
849114d7934SAxel Dörfler				fprintf(stderr, "Error: Resulting target path is too long.\n");
850114d7934SAxel Dörfler				return FSSH_B_BAD_VALUE;
851114d7934SAxel Dörfler			}
852114d7934SAxel Dörfler
853114d7934SAxel Dörfler			strcpy(targetBuffer, target);
854114d7934SAxel Dörfler			strcat(targetBuffer, "/");
855114d7934SAxel Dörfler			strcat(targetBuffer, leaf);
856114d7934SAxel Dörfler			target = targetBuffer;
857114d7934SAxel Dörfler		}
858ada99c21SIngo Weinhold	}
859ada99c21SIngo Weinhold
860114d7934SAxel Dörfler	// check, if the target exists
861114d7934SAxel Dörfler	error = _kern_read_stat(-1, target, false, &st, sizeof(st));
862114d7934SAxel Dörfler	if (error == FSSH_B_OK) {
863114d7934SAxel Dörfler		if (!force) {
864114d7934SAxel Dörfler			fprintf(stderr, "Error: Can't create link. \"%s\" is in the way.\n",
865114d7934SAxel Dörfler				target);
866114d7934SAxel Dörfler			return FSSH_B_FILE_EXISTS;
867114d7934SAxel Dörfler		}
8684f7504e3SIngo Weinhold
869114d7934SAxel Dörfler		// unlink the entry
870114d7934SAxel Dörfler		error = _kern_unlink(-1, target);
871114d7934SAxel Dörfler		if (error != FSSH_B_OK) {
872114d7934SAxel Dörfler			fprintf(stderr, "Error: Failed to remove \"%s\" to make way for "
873114d7934SAxel Dörfler				"link: %s\n", target, fssh_strerror(error));
874114d7934SAxel Dörfler			return error;
875114d7934SAxel Dörfler		}
876ada99c21SIngo Weinhold	}
877ada99c21SIngo Weinhold
878114d7934SAxel Dörfler	// finally create the link
879114d7934SAxel Dörfler	if (symbolic) {
880114d7934SAxel Dörfler		error = _kern_create_symlink(-1, target, source,
881114d7934SAxel Dörfler			FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO);
882114d7934SAxel Dörfler	} else
883114d7934SAxel Dörfler		error = _kern_create_link(target, source);
884114d7934SAxel Dörfler
885114d7934SAxel Dörfler	if (error != FSSH_B_OK) {
886114d7934SAxel Dörfler		fprintf(stderr, "Error: Failed to create link: %s\n",
887114d7934SAxel Dörfler			fssh_strerror(error));
888114d7934SAxel Dörfler	}
889114d7934SAxel Dörfler