1/**
2 * f2fs_format_utils.c
3 *
4 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
5 *             http://www.samsung.com/
6 *
7 * Dual licensed under the GPL or LGPL version 2 licenses.
8 */
9#ifndef _LARGEFILE_SOURCE
10#define _LARGEFILE_SOURCE
11#endif
12#ifndef _LARGEFILE64_SOURCE
13#define _LARGEFILE64_SOURCE
14#endif
15#ifndef _GNU_SOURCE
16#define _GNU_SOURCE
17#endif
18
19#ifndef _FILE_OFFSET_BITS
20#define _FILE_OFFSET_BITS 64
21#endif
22
23#include <f2fs_fs.h>
24
25#include <stdio.h>
26#include <unistd.h>
27#include <stdlib.h>
28#include <stdbool.h>
29#ifdef HAVE_SYS_IOCTL_H
30#include <sys/ioctl.h>
31#endif
32#include <sys/stat.h>
33#include <fcntl.h>
34
35#ifdef HAVE_LINUX_FS_H
36#include <linux/fs.h>
37#endif
38#ifdef HAVE_LINUX_FALLOC_H
39#include <linux/falloc.h>
40#endif
41
42#ifdef __linux__
43#ifndef BLKDISCARD
44#define BLKDISCARD	_IO(0x12,119)
45#endif
46#ifndef BLKSECDISCARD
47#define BLKSECDISCARD	_IO(0x12,125)
48#endif
49#endif
50
51#if defined(FALLOC_FL_PUNCH_HOLE) || defined(BLKDISCARD) || \
52	defined(BLKSECDISCARD)
53static int trim_device(int i)
54{
55	unsigned long long range[2];
56	struct stat *stat_buf;
57	struct device_info *dev = c.devices + i;
58	uint64_t bytes = dev->total_sectors * dev->sector_size;
59	int fd = dev->fd;
60
61	stat_buf = malloc(sizeof(struct stat));
62	if (stat_buf == NULL) {
63		MSG(1, "\tError: Malloc Failed for trim_stat_buf!!!\n");
64		return -1;
65	}
66
67	if (fstat(fd, stat_buf) < 0 ) {
68		MSG(1, "\tError: Failed to get the device stat!!!\n");
69		free(stat_buf);
70		return -1;
71	}
72
73	range[0] = 0;
74	range[1] = bytes;
75
76#if defined(WITH_BLKDISCARD) && defined(BLKDISCARD)
77	MSG(0, "Info: [%s] Discarding device\n", dev->path);
78	if (S_ISREG(stat_buf->st_mode)) {
79#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
80		if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
81				range[0], range[1]) < 0) {
82			MSG(0, "Info: fallocate(PUNCH_HOLE|KEEP_SIZE) is failed\n");
83		}
84#endif
85		free(stat_buf);
86		return 0;
87	} else if (S_ISBLK(stat_buf->st_mode)) {
88		if (dev->zoned_model != F2FS_ZONED_NONE) {
89			free(stat_buf);
90			return f2fs_reset_zones(i);
91		}
92#ifdef BLKSECDISCARD
93		if (ioctl(fd, BLKSECDISCARD, &range) < 0) {
94			MSG(0, "Info: This device doesn't support BLKSECDISCARD\n");
95		} else {
96			MSG(0, "Info: Secure Discarded %lu MB\n",
97					(unsigned long)stat_buf->st_size >> 20);
98			free(stat_buf);
99			return 0;
100		}
101#endif
102		if (ioctl(fd, BLKDISCARD, &range) < 0) {
103			MSG(0, "Info: This device doesn't support BLKDISCARD\n");
104		} else {
105			MSG(0, "Info: Discarded %llu MB\n", range[1] >> 20);
106		}
107	} else {
108		free(stat_buf);
109		return -1;
110	}
111#endif
112	free(stat_buf);
113	return 0;
114}
115#else
116static int trim_device(int UNUSED(i))
117{
118	return 0;
119}
120#endif
121
122#ifdef WITH_ANDROID
123static bool is_wiped_device(int i)
124{
125	struct device_info *dev = c.devices + i;
126	int fd = dev->fd;
127	char *buf, *zero_buf;
128	bool wiped = true;
129	int nblocks = 4096;	/* 16MB size */
130	int j;
131
132	/* let's trim the other devices except the first device */
133	if (i > 0)
134		return false;
135
136	buf = malloc(F2FS_BLKSIZE);
137	if (buf == NULL) {
138		MSG(1, "\tError: Malloc Failed for buf!!!\n");
139		return false;
140	}
141	zero_buf = calloc(1, F2FS_BLKSIZE);
142	if (zero_buf == NULL) {
143		MSG(1, "\tError: Calloc Failed for zero buf!!!\n");
144		free(buf);
145		return false;
146	}
147
148	if (lseek(fd, 0, SEEK_SET) < 0) {
149		free(zero_buf);
150		free(buf);
151		return false;
152	}
153
154	/* check first n blocks */
155	for (j = 0; j < nblocks; j++) {
156		if (read(fd, buf, F2FS_BLKSIZE) != F2FS_BLKSIZE ||
157				memcmp(buf, zero_buf, F2FS_BLKSIZE)) {
158			wiped = false;
159			break;
160		}
161	}
162	free(zero_buf);
163	free(buf);
164
165	if (wiped)
166		MSG(0, "Info: Found all zeros in first %d blocks\n", nblocks);
167	return wiped;
168}
169#else
170static bool is_wiped_device(int UNUSED(i))
171{
172	return false;
173}
174#endif
175
176int f2fs_trim_devices(void)
177{
178	int i;
179
180	for (i = 0; i < c.ndevs; i++) {
181		if (!is_wiped_device(i) && trim_device(i))
182			return -1;
183	}
184	c.trimmed = 1;
185	return 0;
186}
187