1/*
2 * Copyright 2001-2017, Axel D��rfler, axeld@pinc-software.de.
3 * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
6
7
8#include "system_dependencies.h"
9#include "Directory.h"
10#include "Inode.h"
11#include "ShortAttribute.h"
12#include "Symlink.h"
13#include "Volume.h"
14
15#include <file_systems/fs_ops_support.h>
16
17
18#define XFS_IO_SIZE	65536
19
20struct identify_cookie {
21	/*	super_block_struct super_block;
22	*	No structure yet implemented.
23	*/
24	int cookie;
25};
26
27//	#pragma mark - Scanning
28
29
30static float
31xfs_identify_partition(int fd, partition_data *partition, void **_cookie)
32{
33	return B_NOT_SUPPORTED;
34}
35
36
37static status_t
38xfs_scan_partition(int fd, partition_data *partition, void *_cookie)
39{
40	return B_NOT_SUPPORTED;
41}
42
43
44static void
45xfs_free_identify_partition_cookie(partition_data *partition, void *_cookie)
46{
47	dprintf("Unsupported in XFS currently.\n");
48	return;
49}
50
51
52//	#pragma mark -
53
54
55static status_t
56xfs_mount(fs_volume *_volume, const char *device, uint32 flags,
57	const char *args, ino_t *_rootID)
58{
59	TRACE("xfs_mount(): Trying to mount\n");
60
61	Volume *volume = new (std::nothrow) Volume(_volume);
62	if (volume == NULL)
63		return B_NO_MEMORY;
64
65	_volume->private_volume = volume;
66	_volume->ops = &gxfsVolumeOps;
67
68	status_t status = volume->Mount(device, flags);
69	if (status != B_OK) {
70		ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
71		delete volume;
72		_volume->private_volume = NULL;
73		return status;
74	}
75
76	*_rootID = volume->Root();
77	return B_OK;
78}
79
80
81static status_t
82xfs_unmount(fs_volume *_volume)
83{
84	Volume* volume = (Volume*) _volume->private_volume;
85
86	status_t status = volume->Unmount();
87	delete volume;
88	TRACE("xfs_unmount(): Deleted volume");
89	return status;
90}
91
92
93static status_t
94xfs_read_fs_info(fs_volume *_volume, struct fs_info *info)
95{
96	TRACE("XFS_READ_FS_INFO:\n");
97	Volume* volume = (Volume*)_volume->private_volume;
98
99	info->flags = B_FS_IS_READONLY
100					| B_FS_HAS_ATTR | B_FS_IS_PERSISTENT;
101
102	info->io_size = XFS_IO_SIZE;
103	info->block_size = volume->SuperBlock().BlockSize();
104	info->total_blocks = volume->SuperBlock().TotalBlocks();
105	info->free_blocks = volume->SuperBlock().FreeBlocks();
106
107	// Volume name
108	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
109
110	// Filesystem name
111	if(volume->IsVersion5())
112		strlcpy(info->fsh_name, "xfs V5", sizeof(info->fsh_name));
113	else
114		strlcpy(info->fsh_name, "xfs V4", sizeof(info->fsh_name));
115
116	return B_OK;
117}
118
119
120//	#pragma mark -
121
122
123static status_t
124xfs_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *_node, int *_type,
125	uint32 *_flags, bool reenter)
126{
127	TRACE("XFS_GET_VNODE:\n");
128	Volume* volume = (Volume*)_volume->private_volume;
129
130	Inode* inode = new(std::nothrow) Inode(volume, id);
131	if (inode == NULL)
132		return B_NO_MEMORY;
133
134	status_t status = inode->Init();
135	if (status != B_OK) {
136		delete inode;
137		ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
138		return B_NO_INIT;
139	}
140
141	_node->private_node = inode;
142	_node->ops = &gxfsVnodeOps;
143	*_type = inode->Mode();
144	*_flags = 0;
145	TRACE("(%ld)\n", inode->ID());
146	return B_OK;
147}
148
149
150static status_t
151xfs_put_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter)
152{
153	TRACE("XFS_PUT_VNODE:\n");
154	delete (Inode*)_node->private_node;
155	return B_OK;
156}
157
158
159static bool
160xfs_can_page(fs_volume *_volume, fs_vnode *_node, void *_cookie)
161{
162	return B_NOT_SUPPORTED;
163}
164
165
166static status_t
167xfs_read_pages(fs_volume *_volume, fs_vnode *_node, void *_cookie,
168	off_t pos, const iovec *vecs, size_t count, size_t *_numBytes)
169{
170	return B_NOT_SUPPORTED;
171}
172
173
174static status_t
175xfs_io(fs_volume *_volume, fs_vnode *_node, void *_cookie,
176	io_request *request)
177{
178	return B_NOT_SUPPORTED;
179}
180
181
182static status_t
183xfs_get_file_map(fs_volume *_volume, fs_vnode *_node, off_t offset,
184	size_t size, struct file_io_vec *vecs, size_t *_count)
185{
186	return B_NOT_SUPPORTED;
187}
188
189
190//	#pragma mark -
191
192
193static status_t
194xfs_lookup(fs_volume *_volume, fs_vnode *_directory, const char *name,
195	ino_t *_vnodeID)
196{
197	TRACE("XFS_LOOKUP: %p (%s)\n", name, name);
198	Volume* volume = (Volume*)_volume->private_volume;
199	Inode* directory = (Inode*)_directory->private_node;
200
201	if (!directory->IsDirectory())
202		return B_NOT_A_DIRECTORY;
203
204	status_t status = directory->CheckPermissions(X_OK);
205	if (status < B_OK)
206		return status;
207
208	DirectoryIterator* iterator = DirectoryIterator::Init(directory);
209	if (iterator == NULL)
210		return B_BAD_VALUE;
211
212	status = iterator->Lookup(name, strlen(name), (xfs_ino_t*)_vnodeID);
213	if (status != B_OK) {
214		delete iterator;
215		return status;
216	}
217
218	TRACE("XFS_LOOKUP: ID: (%ld)\n", *_vnodeID);
219	status = get_vnode(volume->FSVolume(), *_vnodeID, NULL);
220	TRACE("get_vnode status: (%d)\n", status);
221	return status;
222}
223
224
225static status_t
226xfs_ioctl(fs_volume *_volume, fs_vnode *_node, void *_cookie, uint32 cmd,
227	void *buffer, size_t bufferLength)
228{
229	return B_NOT_SUPPORTED;
230}
231
232
233static status_t
234xfs_read_stat(fs_volume *_volume, fs_vnode *_node, struct stat *stat)
235{
236	Inode* inode = (Inode*)_node->private_node;
237	TRACE("XFS_READ_STAT: id: (%ld)\n", inode->ID());
238	stat->st_dev = inode->GetVolume()->ID();
239	stat->st_ino = inode->ID();
240	stat->st_nlink = 1;
241	stat->st_blksize = XFS_IO_SIZE;
242
243	stat->st_uid = inode->UserId();
244	stat->st_gid = inode->GroupId();
245	stat->st_mode = inode->Mode();
246	stat->st_type = 0;	// TODO
247
248	stat->st_size = inode->Size();
249	stat->st_blocks = inode->BlockCount();
250
251	inode->GetAccessTime(stat->st_atim);
252	inode->GetModificationTime(stat->st_mtim);
253	inode->GetChangeTime(stat->st_ctim);
254
255	// Only version 3 Inodes has creation time
256	if(inode->Version() == 3)
257		inode->GetCreationTime(stat->st_crtim);
258	else
259		inode->GetChangeTime(stat->st_crtim);
260
261	return B_OK;
262}
263
264
265static status_t
266xfs_open(fs_volume * /*_volume*/, fs_vnode *_node, int openMode,
267	void **_cookie)
268{
269	TRACE("XFS_OPEN:\n");
270	Inode* inode = (Inode*)_node->private_node;
271
272	// opening a directory read-only is allowed, although you can't read
273	// any data from it.
274	if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
275		return B_IS_A_DIRECTORY;
276
277	status_t status =  inode->CheckPermissions(open_mode_to_access(openMode)
278		| (openMode & O_TRUNC ? W_OK : 0));
279	if (status != B_OK)
280		return status;
281
282	// Prepare the cookie
283	file_cookie* cookie = new(std::nothrow) file_cookie;
284	if (cookie == NULL)
285		return B_NO_MEMORY;
286	ObjectDeleter<file_cookie> cookieDeleter(cookie);
287
288	cookie->open_mode = openMode & XFS_OPEN_MODE_USER_MASK;
289	cookie->last_size = inode->Size();
290	cookie->last_notification = system_time();
291
292	cookieDeleter.Detach();
293	*_cookie = cookie;
294
295	return B_OK;
296}
297
298
299static status_t
300xfs_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos,
301	void *buffer, size_t *_length)
302{
303	TRACE("Inode::ReadAt: pos:(%ld), *length:(%ld)\n", pos, *_length);
304	Inode* inode = (Inode*)_node->private_node;
305
306	if (!inode->IsFile()) {
307		*_length = 0;
308		return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
309	}
310
311	return inode->ReadAt(pos, (uint8*)buffer, _length);
312}
313
314
315static status_t
316xfs_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
317{
318	return B_OK;
319}
320
321
322static status_t
323xfs_free_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
324{
325	TRACE("XFS_FREE_COOKIE:\n");
326	file_cookie* cookie = (file_cookie*)_cookie;
327	Volume* volume = (Volume*)_volume->private_volume;
328	Inode* inode = (Inode*)_node->private_node;
329
330	if (inode->Size() != cookie->last_size)
331		notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
332
333	delete cookie;
334	return B_OK;
335}
336
337
338static status_t
339xfs_access(fs_volume *_volume, fs_vnode *_node, int accessMode)
340{
341	Inode* inode = (Inode*)_node->private_node;
342	return inode->CheckPermissions(accessMode);
343}
344
345
346static status_t
347xfs_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
348	size_t *_bufferSize)
349{
350	TRACE("XFS_READ_SYMLINK\n");
351
352	Inode* inode = (Inode*)_node->private_node;
353
354	if (!inode->IsSymLink())
355		return B_BAD_VALUE;
356
357	Symlink symlink(inode);
358
359	status_t result = symlink.ReadLink(0, buffer, _bufferSize);
360
361	return result;
362}
363
364
365status_t
366xfs_unlink(fs_volume *_volume, fs_vnode *_directory, const char *name)
367{
368	return B_NOT_SUPPORTED;
369}
370
371
372//	#pragma mark - Directory functions
373
374
375static status_t
376xfs_create_dir(fs_volume *_volume, fs_vnode *_directory, const char *name,
377	int mode)
378{
379	return B_NOT_SUPPORTED;
380}
381
382
383static status_t
384xfs_remove_dir(fs_volume *_volume, fs_vnode *_directory, const char *name)
385{
386	return B_NOT_SUPPORTED;
387}
388
389
390static status_t
391xfs_open_dir(fs_volume * /*_volume*/, fs_vnode *_node, void **_cookie)
392{
393	Inode* inode = (Inode*)_node->private_node;
394	TRACE("XFS_OPEN_DIR: (%ld)\n", inode->ID());
395
396	status_t status = inode->CheckPermissions(R_OK);
397	if (status < B_OK)
398		return status;
399
400	if (!inode->IsDirectory())
401		return B_NOT_A_DIRECTORY;
402
403	DirectoryIterator* iterator = DirectoryIterator::Init(inode);
404	if (iterator == NULL)
405		return B_BAD_VALUE;
406
407	*_cookie = iterator;
408	return status;
409}
410
411
412static status_t
413xfs_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
414	struct dirent *buffer, size_t bufferSize, uint32 *_num)
415{
416	TRACE("XFS_READ_DIR\n");
417	DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
418	Volume* volume = (Volume*)_volume->private_volume;
419
420	uint32 maxCount = *_num;
421	uint32 count = 0;
422
423	while (count < maxCount && (bufferSize > sizeof(struct dirent))) {
424		size_t length = bufferSize - sizeof(struct dirent);
425		xfs_ino_t ino;
426
427		status_t status = iterator->GetNext(buffer->d_name, &length, &ino);
428		if (status == B_ENTRY_NOT_FOUND)
429			break;
430		if (status == B_BUFFER_OVERFLOW) {
431			if (count == 0)
432				return status;
433			break;
434		}
435		if (status != B_OK)
436			return status;
437
438		buffer->d_dev = volume->ID();
439		buffer->d_ino = ino;
440
441		buffer = next_dirent(buffer, length, bufferSize);
442		count++;
443	}
444
445	*_num = count;
446	TRACE("Count: (%d)\n", count);
447	return B_OK;
448}
449
450
451static status_t
452xfs_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
453{
454	return B_NOT_SUPPORTED;
455}
456
457
458static status_t
459xfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/,
460	void * /*_cookie*/)
461{
462	return B_OK;
463}
464
465
466static status_t
467xfs_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
468{
469	delete (DirectoryIterator*)_cookie;
470	return B_OK;
471}
472
473
474static status_t
475xfs_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
476{
477	Inode* inode = (Inode*)_node->private_node;
478	TRACE("%s()\n", __FUNCTION__);
479
480	Attribute* iterator = Attribute::Init(inode);
481	if (iterator == NULL)
482		return B_BAD_VALUE;
483
484	*_cookie = iterator;
485	return B_OK;
486}
487
488
489static status_t
490xfs_close_attr_dir(fs_volume *_volume, fs_vnode *_node, void *cookie)
491{
492	TRACE("%s()\n", __FUNCTION__);
493	return B_OK;
494}
495
496
497static status_t
498xfs_free_attr_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
499{
500	delete (Attribute*)_cookie;
501	return B_OK;
502}
503
504
505static status_t
506xfs_read_attr_dir(fs_volume *_volume, fs_vnode *_node,
507	void *_cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num)
508{
509	TRACE("%s()\n", __FUNCTION__);
510	Attribute* iterator = (Attribute*)_cookie;
511
512	size_t length = bufferSize;
513	status_t status = iterator->GetNext(dirent->d_name, &length);
514	if (status == B_ENTRY_NOT_FOUND) {
515		*_num = 0;
516		return B_OK;
517	}
518
519	if (status != B_OK)
520		return status;
521
522	Volume* volume = (Volume*)_volume->private_volume;
523	Inode* inode = (Inode*)_node->private_node;
524	dirent->d_dev = volume->ID();
525	dirent->d_ino = inode->ID();
526	dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
527	*_num = 1;
528
529	return B_OK;
530}
531
532
533static status_t
534xfs_rewind_attr_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
535{
536	return B_NOT_SUPPORTED;
537}
538
539
540/* attribute operations */
541static status_t
542xfs_create_attr(fs_volume *_volume, fs_vnode *_node,
543	const char *name, uint32 type, int openMode, void **_cookie)
544{
545	return B_NOT_SUPPORTED;
546}
547
548
549static status_t
550xfs_open_attr(fs_volume *_volume, fs_vnode *_node, const char *name,
551	int openMode, void **_cookie)
552{
553	TRACE("%s()\n", __FUNCTION__);
554
555	status_t status;
556
557	Inode* inode = (Inode*)_node->private_node;
558
559	int accessMode = open_mode_to_access(openMode) | (openMode & O_TRUNC ? W_OK : 0);
560	status = inode->CheckPermissions(accessMode);
561	if (status < B_OK)
562		return status;
563
564	Attribute* attribute = Attribute::Init(inode);
565
566	if (attribute == NULL)
567		return B_BAD_VALUE;
568
569	status = attribute->Open(name, openMode, (attr_cookie**)_cookie);
570	delete attribute;
571
572	return status;
573}
574
575
576static status_t
577xfs_close_attr(fs_volume *_volume, fs_vnode *_node,
578	void *cookie)
579{
580	return B_OK;
581}
582
583
584static status_t
585xfs_free_attr_cookie(fs_volume *_volume, fs_vnode *_node, void *cookie)
586{
587	delete (attr_cookie*)cookie;
588	return B_OK;
589}
590
591
592static status_t
593xfs_read_attr(fs_volume *_volume, fs_vnode *_node, void *_cookie,
594	off_t pos, void *buffer, size_t *_length)
595{
596	TRACE("%s()\n", __FUNCTION__);
597
598	attr_cookie* cookie = (attr_cookie*)_cookie;
599	Inode* inode = (Inode*)_node->private_node;
600
601	Attribute* attribute = Attribute::Init(inode);
602
603	if (attribute == NULL)
604		return B_BAD_VALUE;
605
606	status_t status = attribute->Read(cookie, pos, (uint8*)buffer, _length);
607	delete attribute;
608
609	return status;
610}
611
612
613static status_t
614xfs_write_attr(fs_volume *_volume, fs_vnode *_node, void *cookie,
615	off_t pos, const void *buffer, size_t *length)
616{
617	return B_NOT_SUPPORTED;
618}
619
620
621static status_t
622xfs_read_attr_stat(fs_volume *_volume, fs_vnode *_node,
623	void *_cookie, struct stat *stat)
624{
625	TRACE("%s()\n", __FUNCTION__);
626
627	attr_cookie* cookie = (attr_cookie*)_cookie;
628	Inode* inode = (Inode*)_node->private_node;
629
630	Attribute* attribute = Attribute::Init(inode);
631
632	if (attribute == NULL)
633		return B_BAD_VALUE;
634
635	status_t status = attribute->Stat(cookie, *stat);
636	delete attribute;
637
638	return status;
639}
640
641
642static status_t
643xfs_write_attr_stat(fs_volume *_volume, fs_vnode *_node,
644	void *cookie, const struct stat *stat, int statMask)
645{
646	return B_NOT_SUPPORTED;
647}
648
649
650static status_t
651xfs_rename_attr(fs_volume *_volume, fs_vnode *fromVnode,
652	const char *fromName, fs_vnode *toVnode, const char *toName)
653{
654	return B_NOT_SUPPORTED;
655}
656
657
658static status_t
659xfs_remove_attr(fs_volume *_volume, fs_vnode *vnode,
660	const char *name)
661{
662	return B_NOT_SUPPORTED;
663}
664
665
666static uint32
667xfs_get_supported_operations(partition_data *partition, uint32 mask)
668{
669	return B_NOT_SUPPORTED;
670}
671
672
673static status_t
674xfs_initialize(int fd, partition_id partitionID, const char *name,
675	const char *parameterString, off_t partitionSize, disk_job_id job)
676{
677	return B_NOT_SUPPORTED;
678}
679
680
681static status_t
682xfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
683	uint32 blockSize, disk_job_id job)
684{
685	return B_NOT_SUPPORTED;
686}
687
688
689//	#pragma mark -
690
691
692static status_t
693xfs_std_ops(int32 op, ...)
694{
695	switch (op)
696	{
697	case B_MODULE_INIT:
698#ifdef XFS_DEBUGGER_COMMANDS
699		// Perform nothing at the moment
700		// add_debugger_commands();
701#endif
702		return B_OK;
703	case B_MODULE_UNINIT:
704#ifdef XFS_DEBUGGER_COMMANDS
705		// Perform nothing at the moment
706		// remove_debugger_commands();
707#endif
708		return B_OK;
709
710	default:
711		return B_ERROR;
712	}
713}
714
715
716fs_volume_ops gxfsVolumeOps = {
717	&xfs_unmount,
718	&xfs_read_fs_info,
719	NULL,				// write_fs_info()
720	NULL,				// fs_sync,
721	&xfs_get_vnode,
722};
723
724
725fs_vnode_ops gxfsVnodeOps = {
726	/* vnode operations */
727	&xfs_lookup,
728	NULL,				// xfs_get_vnode_name- optional, and we can't do better
729						// than the fallback implementation, so leave as NULL.
730	&xfs_put_vnode,
731	NULL, 				// xfs_remove_vnode,
732
733	/* VM file access */
734	&xfs_can_page,
735	&xfs_read_pages,
736	NULL,				// xfs_write_pages,
737
738	&xfs_io,			// io()
739	NULL,				// cancel_io()
740
741	&xfs_get_file_map,
742
743	&xfs_ioctl,
744	NULL,
745	NULL,				// fs_select
746	NULL,				// fs_deselect
747	NULL,				// fs_fsync,
748
749	&xfs_read_link,
750	NULL,				// fs_create_symlink,
751
752	NULL,				// fs_link,
753	&xfs_unlink,
754	NULL,				// fs_rename,
755
756	&xfs_access,
757	&xfs_read_stat,
758	NULL,				// fs_write_stat,
759	NULL,				// fs_preallocate
760
761	/* file operations */
762	NULL,				// fs_create,
763	&xfs_open,
764	&xfs_close,
765	&xfs_free_cookie,
766	&xfs_read,
767	NULL,				// fs_write,
768
769	/* directory operations */
770	&xfs_create_dir,
771	&xfs_remove_dir,
772	&xfs_open_dir,
773	&xfs_close_dir,
774	&xfs_free_dir_cookie,
775	&xfs_read_dir,
776	&xfs_rewind_dir,
777
778	/* attribute directory operations */
779	&xfs_open_attr_dir,
780	&xfs_close_attr_dir,
781	&xfs_free_attr_dir_cookie,
782	&xfs_read_attr_dir,
783	&xfs_rewind_attr_dir,
784
785	/* attribute operations */
786	&xfs_create_attr,
787	&xfs_open_attr,
788	&xfs_close_attr,
789	&xfs_free_attr_cookie,
790	&xfs_read_attr,
791	&xfs_write_attr,
792	&xfs_read_attr_stat,
793	&xfs_write_attr_stat,
794	&xfs_rename_attr,
795	&xfs_remove_attr,
796};
797
798
799static
800file_system_module_info sxfsFileSystem = {
801	{
802		"file_systems/xfs" B_CURRENT_FS_API_VERSION,
803		0,
804		xfs_std_ops,
805	},
806
807	"xfs",				// short_name
808	"XFS File System",	// pretty_name
809
810	// DDM flags
811	0 |B_DISK_SYSTEM_SUPPORTS_INITIALIZING |B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
812	//	| B_DISK_SYSTEM_SUPPORTS_WRITING
813	,
814
815	// scanning
816	xfs_identify_partition,
817	xfs_scan_partition,
818	xfs_free_identify_partition_cookie,
819	NULL,				// free_partition_content_cookie()
820
821	&xfs_mount,
822
823	/* capability querying operations */
824	&xfs_get_supported_operations,
825
826	NULL,				// validate_resize
827	NULL,				// validate_move
828	NULL,				// validate_set_content_name
829	NULL,				// validate_set_content_parameters
830	NULL,				// validate_initialize,
831
832	/* shadow partition modification */
833	NULL,				// shadow_changed
834
835	/* writing */
836	NULL,				// defragment
837	NULL,				// repair
838	NULL,				// resize
839	NULL,				// move
840	NULL,				// set_content_name
841	NULL,				// set_content_parameters
842	xfs_initialize,
843	xfs_uninitialize};
844
845
846module_info *modules[] = {
847	(module_info *)&sxfsFileSystem,
848	NULL,
849};
850