initial debian packaging
[sven/exfatprogs.git] / tune / tune.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *   Copyright (C) 2019 Namjae Jeon <linkinjeon@kernel.org>
4  */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <getopt.h>
11 #include <errno.h>
12 #include <locale.h>
13
14 #include "exfat_ondisk.h"
15 #include "libexfat.h"
16
17 static void usage(void)
18 {
19         fprintf(stderr, "Usage: tune.exfat\n");
20         fprintf(stderr, "\t-l | --print-label                    Print volume label\n");
21         fprintf(stderr, "\t-L | --volume-label=label             Set volume label\n");
22         fprintf(stderr, "\t-V | --version                        Show version\n");
23         fprintf(stderr, "\t-v | --verbose                        Print debug\n");
24         fprintf(stderr, "\t-h | --help                           Show help\n");
25
26         exit(EXIT_FAILURE);
27 }
28
29 static struct option opts[] = {
30         {"print-label",         no_argument,            NULL,   'l' },
31         {"set-label",           required_argument,      NULL,   'L' },
32         {"version",             no_argument,            NULL,   'V' },
33         {"verbose",             no_argument,            NULL,   'v' },
34         {"help",                no_argument,            NULL,   'h' },
35         {"?",                   no_argument,            NULL,   '?' },
36         {NULL,                  0,                      NULL,    0  }
37 };
38
39 static off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd)
40 {
41         struct pbr *bs;
42         int nbytes;
43         unsigned int cluster_size;
44         off_t root_clu_off;
45
46         bs = (struct pbr *)malloc(sizeof(struct pbr));
47         if (!bs) {
48                 exfat_err("failed to allocate memory\n");
49                 return -ENOMEM;
50         }
51
52         nbytes = exfat_read(bd->dev_fd, bs, sizeof(struct pbr), 0);
53         if (nbytes != sizeof(struct pbr)) {
54                 exfat_err("boot sector read failed: %d\n", errno);
55                 return -1;
56         }
57
58         cluster_size = (1 << bs->bsx.sect_per_clus_bits) * bd->sector_size;
59         root_clu_off = le32_to_cpu(bs->bsx.clu_offset) * bd->sector_size +
60                 le32_to_cpu(bs->bsx.root_cluster - EXFAT_REVERVED_CLUSTERS)
61                 * cluster_size;
62         free(bs);
63
64         return root_clu_off;
65 }
66
67 static int exfat_get_volume_label(struct exfat_blk_dev *bd, off_t root_clu_off)
68 {
69         struct exfat_dentry *vol_entry;
70         char volume_label[VOLUME_LABEL_BUFFER_SIZE];
71         __le16 disk_label[VOLUME_LABEL_MAX_LEN];
72         int nbytes;
73
74         vol_entry = malloc(sizeof(struct exfat_dentry));
75         if (!vol_entry) {
76                 exfat_err("failed to allocate memory\n");
77                 return -ENOMEM;
78         }
79
80         nbytes = exfat_read(bd->dev_fd, vol_entry,
81                 sizeof(struct exfat_dentry), root_clu_off);
82         if (nbytes != sizeof(struct exfat_dentry)) {
83                 exfat_err("volume entry read failed: %d\n", errno);
84                 return -1;
85         }
86
87         memcpy(disk_label, vol_entry->vol_label, sizeof(disk_label));
88         memset(volume_label, 0, sizeof(volume_label));
89         if (exfat_utf16_dec(disk_label, vol_entry->vol_char_cnt*2,
90                 volume_label, sizeof(volume_label)) < 0) {
91                 exfat_err("failed to decode volume label\n");
92                 return -1;
93         }
94
95         exfat_info("label: %s\n", volume_label);
96         return 0;
97 }
98
99 static int exfat_set_volume_label(struct exfat_blk_dev *bd,
100                 char *label_input, off_t root_clu_off)
101 {
102         struct exfat_dentry vol;
103         int nbytes;
104         __u16 volume_label[VOLUME_LABEL_MAX_LEN];
105         int volume_label_len;
106
107         volume_label_len = exfat_utf16_enc(label_input,
108                         volume_label, sizeof(volume_label));
109         if (volume_label_len < 0) {
110                 exfat_err("failed to encode volume label\n");
111                 return -1;
112         }
113
114         vol.type = EXFAT_VOLUME;
115         memset(vol.vol_label, 0, sizeof(vol.vol_label));
116         memcpy(vol.vol_label, volume_label, volume_label_len);
117         vol.vol_char_cnt = volume_label_len/2;
118
119         nbytes = exfat_write(bd->dev_fd, &vol, sizeof(struct exfat_dentry),
120                         root_clu_off);
121         if (nbytes != sizeof(struct exfat_dentry)) {
122                 exfat_err("volume entry write failed: %d\n", errno);
123                 return -1;
124         }
125         fsync(bd->dev_fd);
126
127         exfat_info("new label: %s\n", label_input);
128         return 0;
129 }
130
131 #define EXFAT_GET_LABEL 0x1
132 #define EXFAT_SET_LABEL 0x2
133
134 int main(int argc, char *argv[])
135 {
136         int c;
137         int ret = EXIT_FAILURE;
138         struct exfat_blk_dev bd;
139         struct exfat_user_input ui;
140         bool version_only = false;
141         int flags = 0;
142         char label_input[VOLUME_LABEL_BUFFER_SIZE];
143         off_t root_clu_off;
144
145         init_user_input(&ui);
146
147         if (!setlocale(LC_CTYPE, ""))
148                 exfat_err("failed to init locale/codeset\n");
149
150         opterr = 0;
151         while ((c = getopt_long(argc, argv, "L:lVvh", opts, NULL)) != EOF)
152                 switch (c) {
153                 case 'l':
154                         flags = EXFAT_GET_LABEL;
155                         break;
156                 case 'L':
157                         snprintf(label_input, sizeof(label_input), "%s",
158                                         optarg);
159                         flags = EXFAT_SET_LABEL;
160                         break;
161                 case 'V':
162                         version_only = true;
163                         break;
164                 case 'v':
165                         print_level = EXFAT_DEBUG;
166                         break;
167                 case '?':
168                 case 'h':
169                 default:
170                         usage();
171         }
172
173         show_version();
174         if (version_only)
175                 exit(EXIT_FAILURE);
176
177         if (argc < 3)
178                 usage();
179
180         memset(ui.dev_name, 0, sizeof(ui.dev_name));
181         snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[argc - 1]);
182
183         ret = exfat_get_blk_dev_info(&ui, &bd);
184         if (ret < 0)
185                 goto out;
186
187         root_clu_off = exfat_get_root_entry_offset(&bd);
188         if (root_clu_off < 0)
189                 goto out;
190
191         if (flags == EXFAT_GET_LABEL)
192                 ret = exfat_get_volume_label(&bd, root_clu_off);
193         else if (flags == EXFAT_SET_LABEL)
194                 ret = exfat_set_volume_label(&bd, label_input, root_clu_off);
195
196 out:
197         return ret;
198 }