1/*
2 * Copyright 2020 Suhel Mehta, mehtasuhel@gmail.com
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include <string.h>
7
8#ifdef FS_SHELL
9#include "fssh_api_wrapper.h"
10#endif
11#include <file_systems/fs_ops_support.h>
12
13#include "DirectoryIterator.h"
14#include "Inode.h"
15#include "system_dependencies.h"
16#include "ufs2.h"
17#include "Volume.h"
18
19#define TRACE_UFS2
20#ifdef TRACE_UFS2
21#define TRACE(x...) dprintf("\33[34mufs2:\33[0m " x)
22#else
23#define TRACE(x...) ;
24#endif
25#define ERROR(x...) dprintf("\33[34mufs2:\33[0m " x)
26
27
28struct identify_cookie
29{
30	ufs2_super_block super_block;
31	int cookie;
32};
33
34
35//	#pragma mark - Scanning
36static float
37ufs2_identify_partition(int fd, partition_data *partition, void **_cookie)
38{
39	ufs2_super_block superBlock;
40	status_t status = Volume::Identify(fd, &superBlock);
41	if (status != B_OK)
42		return -1;
43
44	identify_cookie* cookie = new identify_cookie;
45	memcpy(&cookie->super_block, &superBlock, sizeof(ufs2_super_block));
46	*_cookie = cookie;
47
48	return 0.8f;
49}
50
51
52static status_t
53ufs2_scan_partition(int fd, partition_data *partition, void *_cookie)
54{
55	identify_cookie* cookie = (identify_cookie*)_cookie;
56
57	partition->status = B_PARTITION_VALID;
58	partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY;
59	partition->block_size = cookie->super_block.fs_bsize;
60	partition->content_size = cookie->super_block.fs_fsize
61		* cookie->super_block.fs_size;
62	partition->content_name = strdup(cookie->super_block.fs_volname);
63	if (partition->content_name == NULL)
64		return B_NO_MEMORY;
65
66	return B_OK;
67}
68
69
70static void
71ufs2_free_identify_partition_cookie(partition_data *partition, void *_cookie)
72{
73	identify_cookie* cookie = (identify_cookie*)_cookie;
74	delete cookie;
75	return;
76}
77
78
79//	#pragma mark -
80static status_t
81ufs2_mount(fs_volume *_volume, const char *device, uint32 flags,
82		  const char *args, ino_t *_rootID)
83{
84	TRACE("Tracing mount()\n");
85	Volume* volume = new(std::nothrow) Volume(_volume);
86	if (volume == NULL)
87		return B_NO_MEMORY;
88
89	_volume->private_volume = volume;
90	_volume->ops = &gUfs2VolumeOps;
91	*_rootID = UFS2_ROOT;
92	status_t status = volume->Mount(device, flags);
93	if (status != B_OK){
94		ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
95		delete volume;
96		return status;
97	}
98	return B_OK;
99}
100
101
102static status_t
103ufs2_unmount(fs_volume *_volume)
104{
105	Volume* volume = (Volume *)_volume->private_volume;
106
107	status_t status = volume->Unmount();
108	delete volume;
109
110	return status;
111}
112
113
114static status_t
115ufs2_read_fs_info(fs_volume *_volume, struct fs_info *info)
116{
117	Volume* volume = (Volume*)_volume->private_volume;
118
119	// File system flags
120	info->flags = B_FS_IS_PERSISTENT
121		| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
122	info->io_size = 65536;
123	info->block_size = volume->SuperBlock().fs_fsize;
124	info->total_blocks = volume->SuperBlock().fs_size;
125	info->free_blocks = volume->SuperBlock().fs_cstotal.cs_nbfree;
126
127	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
128	strlcpy(info->fsh_name, "UFS2", sizeof(info->fsh_name));
129
130	return B_OK;
131}
132
133
134//	#pragma mark -
135
136static status_t
137ufs2_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *_node, int *_type,
138			  uint32 *_flags, bool reenter)
139{
140	Volume* volume = (Volume*)_volume->private_volume;
141
142	Inode* inode = new(std::nothrow) Inode(volume, id);
143	if (inode == NULL)
144		return B_NO_MEMORY;
145
146	status_t status = inode->InitCheck();
147	if (status != B_OK) {
148		delete inode;
149		ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
150		return status;
151	}
152	_node->private_node = inode;
153	_node->ops = &gufs2VnodeOps;
154	*_type = inode->Mode();
155	*_flags = 0;
156
157	return B_OK;
158
159}
160
161
162static status_t
163ufs2_put_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter)
164{
165	return B_NOT_SUPPORTED;
166}
167
168
169static bool
170ufs2_can_page(fs_volume *_volume, fs_vnode *_node, void *_cookie)
171{
172	return false;
173}
174
175
176static status_t
177ufs2_read_pages(fs_volume *_volume, fs_vnode *_node, void *_cookie,
178			   off_t pos, const iovec *vecs, size_t count, size_t *_numBytes)
179{
180	return B_NOT_SUPPORTED;
181}
182
183
184static status_t
185ufs2_io(fs_volume *_volume, fs_vnode *_node, void *_cookie,
186	   io_request *request)
187{
188	return B_NOT_SUPPORTED;
189}
190
191
192static status_t
193ufs2_get_file_map(fs_volume *_volume, fs_vnode *_node, off_t offset,
194				 size_t size, struct file_io_vec *vecs, size_t *_count)
195{
196	return B_NOT_SUPPORTED;
197}
198
199
200//	#pragma mark -
201
202static status_t
203ufs2_lookup(fs_volume *_volume, fs_vnode *_directory, const char *name,
204		   ino_t *_vnodeID)
205{
206	Volume* volume = (Volume*)_volume->private_volume;
207	Inode* directory = (Inode*)_directory->private_node;
208
209	// check access permissions
210	status_t status = directory->CheckPermissions(X_OK);
211	if (status < B_OK)
212		return status;
213
214	status = DirectoryIterator(directory).Lookup(name, _vnodeID);
215	if (status != B_OK)
216		return status;
217
218	status = get_vnode(volume->FSVolume(), *_vnodeID, NULL);
219	return status;
220}
221
222
223static status_t
224ufs2_ioctl(fs_volume *_volume, fs_vnode *_node, void *_cookie, uint32 cmd,
225		  void *buffer, size_t bufferLength)
226{
227	return B_NOT_SUPPORTED;
228}
229
230
231static status_t
232ufs2_read_stat(fs_volume *_volume, fs_vnode *_node, struct stat *stat)
233{
234	Inode* inode = (Inode*)_node->private_node;
235	stat->st_dev = inode->GetVolume()->ID();
236	stat->st_ino = inode->ID();
237	stat->st_nlink = inode->LinkCount();
238	stat->st_blksize = 65536;
239
240	stat->st_uid = inode->UserID();
241	stat->st_gid = inode->GroupID();
242	stat->st_mode = inode->Mode();
243	stat->st_type = 0;
244
245	inode->GetAccessTime(stat->st_atim);
246	inode->GetModificationTime(stat->st_mtim);
247	inode->GetChangeTime(stat->st_ctim);
248	inode->GetCreationTime(stat->st_crtim);
249
250	stat->st_size = inode->Size();
251	stat->st_blocks = inode->NumBlocks();
252
253	return B_OK;
254}
255
256
257static status_t
258ufs2_open(fs_volume * _volume, fs_vnode *_node, int openMode,
259		 void **_cookie)
260{
261	//Volume* volume = (Volume*)_volume->private_volume;
262	Inode* inode = (Inode*)_node->private_node;
263
264	// opening a directory read-only is allowed, although you can't read
265	// any data from it.
266	if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
267		return B_IS_A_DIRECTORY;
268
269	status_t status =  inode->CheckPermissions(open_mode_to_access(openMode)
270		| (openMode & O_TRUNC ? W_OK : 0));
271	if (status != B_OK)
272		return status;
273
274	file_cookie* cookie = new(std::nothrow) file_cookie;
275	if (cookie == NULL)
276		return B_NO_MEMORY;
277	ObjectDeleter<file_cookie> cookieDeleter(cookie);
278
279	cookie->last_size = inode->Size();
280	cookie->last_notification = system_time();
281
282//	fileCacheEnabler.Detach();
283	cookieDeleter.Detach();
284	*_cookie = cookie;
285	return B_OK;
286}
287
288
289static status_t
290ufs2_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos,
291		 void *buffer, size_t *_length)
292{
293	Inode* inode = (Inode*)_node->private_node;
294
295	if (!inode->IsFile()) {
296		*_length = 0;
297		return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
298	}
299
300	return inode->ReadAt(pos, (uint8*)buffer, _length);
301}
302
303
304static status_t
305ufs2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
306{
307	return B_OK;
308}
309
310
311static status_t
312ufs2_free_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
313{
314	file_cookie* cookie = (file_cookie*)_cookie;
315
316	delete cookie;
317	return B_OK;
318}
319
320static status_t
321ufs2_access(fs_volume *_volume, fs_vnode *_node, int accessMode)
322{
323	Inode* inode = (Inode*)_node->private_node;
324	return inode->CheckPermissions(accessMode);
325}
326
327
328static status_t
329ufs2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
330			  size_t *_bufferSize)
331{
332	Inode* inode = (Inode*)_node->private_node;
333
334	return inode->ReadLink(buffer, _bufferSize);
335}
336
337
338status_t
339ufs2_unlink(fs_volume *_volume, fs_vnode *_directory, const char *name)
340{
341	return B_NOT_SUPPORTED;
342}
343
344
345//	#pragma mark - Directory functions
346
347static status_t
348ufs2_create_dir(fs_volume *_volume, fs_vnode *_directory, const char *name,
349			   int mode)
350{
351	return B_NOT_SUPPORTED;
352}
353
354
355static status_t
356ufs2_remove_dir(fs_volume *_volume, fs_vnode *_directory, const char *name)
357{
358	return B_NOT_SUPPORTED;
359}
360
361
362static status_t
363ufs2_open_dir(fs_volume * /*_volume*/, fs_vnode *_node, void **_cookie)
364{
365	Inode* inode = (Inode*)_node->private_node;
366
367	status_t status = inode->CheckPermissions(R_OK);
368	if (status < B_OK)
369		return status;
370
371	if (!inode->IsDirectory())
372		return B_NOT_A_DIRECTORY;
373
374	DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
375	if (iterator == NULL)
376		return B_NO_MEMORY;
377
378	*_cookie = iterator;
379	return B_OK;
380}
381
382
383static status_t
384ufs2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
385			 struct dirent *dirent, size_t bufferSize, uint32 *_num)
386{
387	DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
388	Volume* volume = (Volume*)_volume->private_volume;
389
390	uint32 maxCount = *_num;
391	uint32 count = 0;
392
393	while (count < maxCount
394			&& (bufferSize >= sizeof(struct dirent) + B_FILE_NAME_LENGTH)) {
395		size_t length = bufferSize - offsetof(struct dirent, d_name);
396		ino_t iNodeNo;
397
398		status_t status = iterator->GetNext(dirent->d_name, &length, &iNodeNo);
399		if (status == B_ENTRY_NOT_FOUND)
400			break;
401		if (status == B_BUFFER_OVERFLOW) {
402			if (count == 0)
403				return status;
404			break;
405		}
406		if (status != B_OK)
407			return status;
408
409		dirent->d_dev = volume->ID();
410		dirent->d_ino = iNodeNo;
411		dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
412		bufferSize -= dirent->d_reclen;
413		dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen);
414		count++;
415	}
416
417	*_num = count;
418	return B_OK;
419
420}
421
422
423static status_t
424ufs2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
425{
426	DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
427	return iterator->Rewind();
428}
429
430
431static status_t
432ufs2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/,
433			  void * /*_cookie*/)
434{
435	return B_OK;
436}
437
438
439static status_t
440ufs2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
441{
442	delete (DirectoryIterator*)_cookie;
443	return B_OK;
444}
445
446
447static status_t
448ufs2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
449{
450	return B_NOT_SUPPORTED;
451}
452
453
454static status_t
455ufs2_close_attr_dir(fs_volume *_volume, fs_vnode *_node, void *cookie)
456{
457	return B_NOT_SUPPORTED;
458}
459
460
461static status_t
462ufs2_free_attr_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
463{
464	return B_NOT_SUPPORTED;
465}
466
467
468static status_t
469ufs2_read_attr_dir(fs_volume *_volume, fs_vnode *_node,
470				  void *_cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num)
471{
472	return B_NOT_SUPPORTED;
473}
474
475
476static status_t
477ufs2_rewind_attr_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie)
478{
479	return B_NOT_SUPPORTED;
480}
481
482
483/* attribute operations */
484static status_t
485ufs2_create_attr(fs_volume *_volume, fs_vnode *_node,
486				const char *name, uint32 type, int openMode, void **_cookie)
487{
488	return B_NOT_SUPPORTED;
489}
490
491
492static status_t
493ufs2_open_attr(fs_volume *_volume, fs_vnode *_node, const char *name,
494			  int openMode, void **_cookie)
495{
496	return B_NOT_SUPPORTED;
497}
498
499
500static status_t
501ufs2_close_attr(fs_volume *_volume, fs_vnode *_node,
502			   void *cookie)
503{
504	return B_NOT_SUPPORTED;
505}
506
507
508static status_t
509ufs2_free_attr_cookie(fs_volume *_volume, fs_vnode *_node,
510					 void *cookie)
511{
512	return B_NOT_SUPPORTED;
513}
514
515
516static status_t
517ufs2_read_attr(fs_volume *_volume, fs_vnode *_node, void *_cookie,
518			  off_t pos, void *buffer, size_t *_length)
519{
520	return B_NOT_SUPPORTED;
521}
522
523
524static status_t
525ufs2_write_attr(fs_volume *_volume, fs_vnode *_node, void *cookie,
526			   off_t pos, const void *buffer, size_t *length)
527{
528	return B_NOT_SUPPORTED;
529}
530
531
532static status_t
533ufs2_read_attr_stat(fs_volume *_volume, fs_vnode *_node,
534				   void *_cookie, struct stat *stat)
535{
536	return B_NOT_SUPPORTED;
537}
538
539
540static status_t
541ufs2_write_attr_stat(fs_volume *_volume, fs_vnode *_node,
542					void *cookie, const struct stat *stat, int statMask)
543{
544	return B_NOT_SUPPORTED;
545}
546
547
548static status_t
549ufs2_rename_attr(fs_volume *_volume, fs_vnode *fromVnode,
550				const char *fromName, fs_vnode *toVnode, const char *toName)
551{
552	return B_NOT_SUPPORTED;
553}
554
555
556static status_t
557ufs2_remove_attr(fs_volume *_volume, fs_vnode *vnode,
558				const char *name)
559{
560	return B_NOT_SUPPORTED;
561}
562
563
564//	#pragma mark -
565
566static status_t
567ufs2_std_ops(int32 op, ...)
568{
569	switch (op)
570	{
571	case B_MODULE_INIT:
572#ifdef ufs2_DEBUGGER_COMMANDS
573		// Perform nothing at the moment
574		// add_debugger_commands();
575#endif
576		return B_OK;
577	case B_MODULE_UNINIT:
578#ifdef ufs2_DEBUGGER_COMMANDS
579		// Perform nothing at the moment
580		// remove_debugger_commands();
581#endif
582		return B_OK;
583
584	default:
585		return B_ERROR;
586	}
587}
588
589fs_volume_ops gUfs2VolumeOps = {
590	&ufs2_unmount,
591	&ufs2_read_fs_info,
592	NULL, //write_fs_info()
593	NULL, // fs_sync,
594	&ufs2_get_vnode,
595};
596
597fs_vnode_ops gufs2VnodeOps = {
598	/* vnode operations */
599	&ufs2_lookup,
600	NULL, // ufs2_get_vnode_name - optional, and we can't do better than the
601		  // fallback implementation, so leave as NULL.
602	&ufs2_put_vnode,
603	NULL, // ufs2_remove_vnode,
604
605	/* VM file access */
606	&ufs2_can_page,
607	&ufs2_read_pages,
608	NULL, // ufs2_write_pages,
609
610	&ufs2_io, // io()
611	NULL,	// cancel_io()
612
613	&ufs2_get_file_map,
614
615	&ufs2_ioctl,
616	NULL,
617	NULL, // fs_select
618	NULL, // fs_deselect
619	NULL, // fs_fsync,
620
621	&ufs2_read_link,
622	NULL, // fs_create_symlink,
623
624	NULL, // fs_link,
625	&ufs2_unlink,
626	NULL, // fs_rename,
627
628	&ufs2_access,
629	&ufs2_read_stat,
630	NULL, // fs_write_stat,
631	NULL, // fs_preallocate
632
633	/* file operations */
634	NULL, // fs_create,
635	&ufs2_open,
636	&ufs2_close,
637	&ufs2_free_cookie,
638	&ufs2_read,
639	NULL, //	fs_write,
640
641	/* directory operations */
642	&ufs2_create_dir,
643	&ufs2_remove_dir,
644	&ufs2_open_dir,
645	&ufs2_close_dir,
646	&ufs2_free_dir_cookie,
647	&ufs2_read_dir,
648	&ufs2_rewind_dir,
649
650	/* attribute directory operations */
651	&ufs2_open_attr_dir,
652	&ufs2_close_attr_dir,
653	&ufs2_free_attr_dir_cookie,
654	&ufs2_read_attr_dir,
655	&ufs2_rewind_attr_dir,
656
657	/* attribute operations */
658	&ufs2_create_attr,
659	&ufs2_open_attr,
660	&ufs2_close_attr,
661	&ufs2_free_attr_cookie,
662	&ufs2_read_attr,
663	&ufs2_write_attr,
664	&ufs2_read_attr_stat,
665	&ufs2_write_attr_stat,
666	&ufs2_rename_attr,
667	&ufs2_remove_attr,
668};
669
670
671static file_system_module_info sufs2FileSystem = {
672	{
673		"file_systems/ufs2" B_CURRENT_FS_API_VERSION,
674		0,
675		ufs2_std_ops,
676	},
677
678	"ufs2",			   // short_name
679	"Unix Filesystem 2", // pretty_name
680
681	// DDM flags
682	B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
683	//	| B_DISK_SYSTEM_SUPPORTS_WRITING
684	,
685
686	// scanning
687	ufs2_identify_partition,
688	ufs2_scan_partition,
689	ufs2_free_identify_partition_cookie,
690	NULL, // free_partition_content_cookie()
691
692	&ufs2_mount,
693};
694
695module_info *modules[] = {
696	(module_info *)&sufs2FileSystem,
697	NULL,
698};
699