]> git.sven.stormbind.net Git - sven/fuse-exfat.git/blob - attrib/main.c
Update debian/copyright. Drop year from my own copyright line.
[sven/fuse-exfat.git] / attrib / main.c
1 /*
2         main.c (08.11.20)
3         Prints or changes exFAT file attributes
4
5         Free exFAT implementation.
6         Copyright (C) 2020-2023  Endless OS Foundation LLC
7
8         This program is free software; you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 2 of the License, or
11         (at your option) any later version.
12
13         This program is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License along
19         with this program; if not, write to the Free Software Foundation, Inc.,
20         51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include <exfat.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 static void usage(const char* prog)
29 {
30         fprintf(stderr,
31                 "Display current attributes:\n"
32                 "  %1$s -d <device> <file>\n"
33                 "\n"
34                 "Set attributes:\n"
35                 "  %1$s [FLAGS] -d <device> <file>\n"
36                 "\n"
37                 "Flags:\n"
38                 "  -r    Set read-only flag\n"
39                 "  -R    Clear read-only flag\n"
40                 "  -i    Set hidden flag\n"
41                 "  -I    Clear hidden flag\n"
42                 "  -s    Set system flag\n"
43                 "  -S    Clear system flag\n"
44                 "  -a    Set archive flag\n"
45                 "  -A    Clear archive flag\n"
46                 "\n"
47                 "  -h    Display this help message\n"
48                 "  -V    Display version information\n",
49                 prog);
50         exit(1);
51 }
52
53 static void print_attribute(uint16_t attribs, uint16_t attrib,
54                 const char* label)
55 {
56         printf("%9s: %s\n", label, (attribs & attrib) ? "yes" : "no");
57 }
58
59 static int attribute(struct exfat* ef, struct exfat_node* node,
60                 uint16_t add_flags, uint16_t clear_flags)
61 {
62         if ((add_flags | clear_flags) != 0)
63         {
64                 uint16_t attrib = node->attrib;
65
66                 attrib |= add_flags;
67                 attrib &= ~clear_flags;
68
69                 if (node->attrib != attrib)
70                 {
71                         int ret;
72
73                         node->attrib = attrib;
74                         node->is_dirty = true;
75
76                         ret = exfat_flush_node(ef, node);
77                         if (ret != 0)
78                         {
79                                 char buffer[EXFAT_UTF8_NAME_BUFFER_MAX];
80
81                                 exfat_get_name(node, buffer);
82                                 exfat_error("failed to flush changes to '%s': %s", buffer,
83                                                 strerror(-ret));
84                                 return 1;
85                         }
86                 }
87         }
88         else
89         {
90                 print_attribute(node->attrib, EXFAT_ATTRIB_RO, "Read-only");
91                 print_attribute(node->attrib, EXFAT_ATTRIB_HIDDEN, "Hidden");
92                 print_attribute(node->attrib, EXFAT_ATTRIB_SYSTEM, "System");
93                 print_attribute(node->attrib, EXFAT_ATTRIB_ARCH, "Archive");
94                 /* read-only attributes */
95                 print_attribute(node->attrib, EXFAT_ATTRIB_VOLUME, "Volume");
96                 print_attribute(node->attrib, EXFAT_ATTRIB_DIR, "Directory");
97         }
98
99         return 0;
100 }
101
102 int main(int argc, char* argv[])
103 {
104         int opt;
105         int ret;
106         const char* spec = NULL;
107         const char* options = "";
108         const char* file_path = NULL;
109         struct exfat ef;
110         struct exfat_node* node;
111         uint16_t add_flags = 0;
112         uint16_t clear_flags = 0;
113
114         while ((opt = getopt(argc, argv, "d:rRiIsSaAhV")) != -1)
115         {
116                 switch (opt)
117                 {
118                 case 'V':
119                         printf("exfatattrib %s\n", VERSION);
120                         puts("Copyright (C) 2011-2023  Andrew Nayenko");
121                         puts("Copyright (C) 2020-2023  Endless OS Foundation LLC");
122                         return 0;
123                 /*
124                         The path to the unmounted exFAT partition is a (mandatory) named
125                         option rather than a positional parameter. If the FUSE file system
126                         ever gains an ioctl to get and set attributes, this option could be
127                         made optional, and this tool taught to use the ioctl.
128                 */
129                 case 'd':
130                         spec = optarg;
131                         break;
132                 case 'r':
133                         add_flags |= EXFAT_ATTRIB_RO;
134                         break;
135                 case 'R':
136                         clear_flags |= EXFAT_ATTRIB_RO;
137                         break;
138                 /* "-h[elp]" is taken; i is the second letter of "hidden" and
139                    its synonym "invisible" */
140                 case 'i':
141                         add_flags |= EXFAT_ATTRIB_HIDDEN;
142                         break;
143                 case 'I':
144                         clear_flags |= EXFAT_ATTRIB_HIDDEN;
145                         break;
146                 case 's':
147                         add_flags |= EXFAT_ATTRIB_SYSTEM;
148                         break;
149                 case 'S':
150                         clear_flags |= EXFAT_ATTRIB_SYSTEM;
151                         break;
152                 case 'a':
153                         add_flags |= EXFAT_ATTRIB_ARCH;
154                         break;
155                 case 'A':
156                         clear_flags |= EXFAT_ATTRIB_ARCH;
157                         break;
158                 default:
159                         usage(argv[0]);
160                 }
161         }
162
163         if ((add_flags & clear_flags) != 0)
164         {
165                 exfat_error("can't set and clear the same flag");
166                 return 1;
167         }
168
169         if (spec == NULL || argc - optind != 1)
170                 usage(argv[0]);
171
172         file_path = argv[optind];
173
174         if ((add_flags | clear_flags) == 0)
175                 options = "ro";
176
177         ret = exfat_mount(&ef, spec, options);
178         if (ret != 0)
179         {
180                 exfat_error("failed to mount %s: %s", spec, strerror(-ret));
181                 return 1;
182         }
183
184         ret = exfat_lookup(&ef, &node, file_path);
185         if (ret != 0)
186         {
187                 exfat_error("failed to look up '%s': %s", file_path, strerror(-ret));
188                 exfat_unmount(&ef);
189                 return 1;
190         }
191
192         ret = attribute(&ef, node, add_flags, clear_flags);
193
194         exfat_put_node(&ef, node);
195         exfat_unmount(&ef);
196
197         return ret;
198 }