1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2002 Juli Mallett.  All rights reserved.
5 *
6 * This software was written by Juli Mallett <jmallett@FreeBSD.org> for the
7 * FreeBSD project.  Redistribution and use in source and binary forms, with
8 * or without modification, are permitted provided that the following
9 * conditions are met:
10 *
11 * 1. Redistribution of source code must retain the above copyright notice,
12 *    this list of conditions and the following disclaimer.
13 * 2. Redistribution in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/mount.h>
35#include <sys/disklabel.h>
36#include <sys/stat.h>
37
38#include <ufs/ufs/ufsmount.h>
39#include <ufs/ufs/dinode.h>
40#include <ufs/ffs/fs.h>
41
42#include <errno.h>
43#include <fcntl.h>
44#include <fstab.h>
45#include <paths.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50
51#include <libufs.h>
52
53/* Internally, track the 'name' value, it's ours. */
54#define	MINE_NAME	0x01
55/* Track if its fd points to a writable device. */
56#define	MINE_WRITE	0x02
57
58int
59ufs_disk_close(struct uufsd *disk)
60{
61	ERROR(disk, NULL);
62	close(disk->d_fd);
63	disk->d_fd = -1;
64	if (disk->d_inoblock != NULL) {
65		free(disk->d_inoblock);
66		disk->d_inoblock = NULL;
67	}
68	if (disk->d_mine & MINE_NAME) {
69		free((char *)(uintptr_t)disk->d_name);
70		disk->d_name = NULL;
71	}
72	if (disk->d_si != NULL) {
73		free(disk->d_si->si_csp);
74		free(disk->d_si);
75		disk->d_si = NULL;
76	}
77	return (0);
78}
79
80int
81ufs_disk_fillout(struct uufsd *disk, const char *name)
82{
83	if (ufs_disk_fillout_blank(disk, name) == -1) {
84		return (-1);
85	}
86	if (sbread(disk) == -1) {
87		ERROR(disk, "could not read superblock to fill out disk");
88		ufs_disk_close(disk);
89		return (-1);
90	}
91	return (0);
92}
93
94int
95ufs_disk_fillout_blank(struct uufsd *disk, const char *name)
96{
97	struct stat st;
98	struct fstab *fs;
99	struct statfs sfs;
100	const char *oname;
101	char dev[MAXPATHLEN];
102	int fd, ret;
103
104	ERROR(disk, NULL);
105
106	oname = name;
107again:	if ((ret = stat(name, &st)) < 0) {
108		if (*name != '/') {
109			snprintf(dev, sizeof(dev), "%s%s", _PATH_DEV, name);
110			name = dev;
111			goto again;
112		}
113		/*
114		 * The given object doesn't exist, but don't panic just yet -
115		 * it may be still mount point listed in /etc/fstab, but without
116		 * existing corresponding directory.
117		 */
118		name = oname;
119	}
120	if (ret >= 0 && S_ISREG(st.st_mode)) {
121		/* Possibly a disk image, give it a try.  */
122		;
123	} else if (ret >= 0 && S_ISCHR(st.st_mode)) {
124		/* This is what we need, do nothing. */
125		;
126	} else if ((fs = getfsfile(name)) != NULL) {
127		/*
128		 * The given mount point is listed in /etc/fstab.
129		 * It is possible that someone unmounted file system by hand
130		 * and different file system is mounted on this mount point,
131		 * but we still prefer /etc/fstab entry, because on the other
132		 * hand, there could be /etc/fstab entry for this mount
133		 * point, but file system is not mounted yet (eg. noauto) and
134		 * statfs(2) will point us at different file system.
135		 */
136		name = fs->fs_spec;
137	} else if (ret >= 0 && S_ISDIR(st.st_mode)) {
138		/*
139		 * The mount point is not listed in /etc/fstab, so it may be
140		 * file system mounted by hand.
141		 */
142		if (statfs(name, &sfs) < 0) {
143			ERROR(disk, "could not find special device");
144			return (-1);
145		}
146		strlcpy(dev, sfs.f_mntfromname, sizeof(dev));
147		name = dev;
148	} else {
149		ERROR(disk, "could not find special device");
150		return (-1);
151	}
152	fd = open(name, O_RDONLY);
153	if (fd == -1) {
154		ERROR(disk, "could not open special device");
155		return (-1);
156	}
157
158	disk->d_bsize = 1;
159	disk->d_ccg = 0;
160	disk->d_fd = fd;
161	disk->d_inoblock = NULL;
162	disk->d_inomin = 0;
163	disk->d_inomax = 0;
164	disk->d_lcg = 0;
165	disk->d_mine = 0;
166	disk->d_ufs = 0;
167	disk->d_error = NULL;
168	disk->d_si = NULL;
169
170	if (oname != name) {
171		name = strdup(name);
172		if (name == NULL) {
173			ERROR(disk, "could not allocate memory for disk name");
174			return (-1);
175		}
176		disk->d_mine |= MINE_NAME;
177	}
178	disk->d_name = name;
179
180	return (0);
181}
182
183int
184ufs_disk_write(struct uufsd *disk)
185{
186	int fd;
187
188	ERROR(disk, NULL);
189
190	if (disk->d_mine & MINE_WRITE)
191		return (0);
192
193	fd = open(disk->d_name, O_RDWR);
194	if (fd < 0) {
195		ERROR(disk, "failed to open disk for writing");
196		return (-1);
197	}
198
199	close(disk->d_fd);
200	disk->d_fd = fd;
201	disk->d_mine |= MINE_WRITE;
202
203	return (0);
204}
205