sync'ing up for 3.0alpha20 release
[tprouty/samba.git] / source / client / smbmnt.c
1 /*
2  *  smbmnt.c
3  *
4  *  Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke
5  *  extensively modified by Tridge
6  *
7  */
8
9 #include "includes.h"
10
11 #include <mntent.h>
12 #include <sys/utsname.h>
13
14 #include <asm/types.h>
15 #include <asm/posix_types.h>
16 #include <linux/smb.h>
17 #include <linux/smb_mount.h>
18 #include <asm/unistd.h>
19
20 #ifndef MS_MGC_VAL
21 /* This may look strange but MS_MGC_VAL is what we are looking for and
22         is what we need from <linux/fs.h> under libc systems and is
23         provided in standard includes on glibc systems.  So...  We
24         switch on what we need...  */
25 #include <linux/fs.h>
26 #endif
27
28 static uid_t mount_uid;
29 static gid_t mount_gid;
30 static int mount_ro;
31 static unsigned mount_fmask;
32 static unsigned mount_dmask;
33 static int user_mount;
34 static char *options;
35
36 static void
37 help(void)
38 {
39         printf("\n");
40         printf("Usage: smbmnt mount-point [options]\n");
41         printf("Version %s\n\n",VERSION);
42         printf("-s share       share name on server\n"
43                "-r             mount read-only\n"
44                "-u uid         mount as uid\n"
45                "-g gid         mount as gid\n"
46                "-f mask        permission mask for files\n"
47                "-d mask        permission mask for directories\n"
48                "-o options     name=value, list of options\n"
49                "-h             print this help text\n");
50 }
51
52 static int
53 parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share)
54 {
55         int opt;
56
57         while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF)
58         {
59                 switch (opt)
60                 {
61                 case 's':
62                         *share = optarg;
63                         break;
64                 case 'u':
65                         if (!user_mount) {
66                                 mount_uid = strtol(optarg, NULL, 0);
67                         }
68                         break;
69                 case 'g':
70                         if (!user_mount) {
71                                 mount_gid = strtol(optarg, NULL, 0);
72                         }
73                         break;
74                 case 'r':
75                         mount_ro = 1;
76                         break;
77                 case 'f':
78                         mount_fmask = strtol(optarg, NULL, 8);
79                         break;
80                 case 'd':
81                         mount_dmask = strtol(optarg, NULL, 8);
82                         break;
83                 case 'o':
84                         options = optarg;
85                         break;
86                 default:
87                         return -1;
88                 }
89         }
90         return 0;
91         
92 }
93
94 static char *
95 fullpath(const char *p)
96 {
97         char path[MAXPATHLEN];
98
99         if (strlen(p) > MAXPATHLEN-1) {
100                 return NULL;
101         }
102
103         if (realpath(p, path) == NULL) {
104                 fprintf(stderr,"Failed to find real path for mount point\n");
105                 exit(1);
106         }
107         return strdup(path);
108 }
109
110 /* Check whether user is allowed to mount on the specified mount point. If it's
111    OK then we change into that directory - this prevents race conditions */
112 static int mount_ok(char *mount_point)
113 {
114         struct stat st;
115
116         if (chdir(mount_point) != 0) {
117                 return -1;
118         }
119
120         if (stat(".", &st) != 0) {
121                 return -1;
122         }
123
124         if (!S_ISDIR(st.st_mode)) {
125                 errno = ENOTDIR;
126                 return -1;
127         }
128
129         if ((getuid() != 0) && 
130             ((getuid() != st.st_uid) || 
131              ((st.st_mode & S_IRWXU) != S_IRWXU))) {
132                 errno = EPERM;
133                 return -1;
134         }
135
136         return 0;
137 }
138
139 /* Tries to mount using the appropriate format. For 2.2 the struct,
140    for 2.4 the ascii version. */
141 static int
142 do_mount(char *share_name, unsigned int flags, struct smb_mount_data *data)
143 {
144         pstring opts;
145         struct utsname uts;
146         char *release, *major, *minor;
147         char *data1, *data2;
148
149         uname(&uts);
150         release = uts.release;
151         major = strtok(release, ".");
152         minor = strtok(NULL, ".");
153         if (major && minor && atoi(major) == 2 && atoi(minor) < 4) {
154                 /* < 2.4, assume struct */
155                 data1 = (char *) data;
156                 data2 = opts;
157         } else {
158                 /* >= 2.4, assume ascii but fall back on struct */
159                 data1 = opts;
160                 data2 = (char *) data;
161         }
162
163         slprintf(opts, sizeof(opts)-1,
164                  "version=7,uid=%d,gid=%d,file_mode=0%o,dir_mode=0%o,%s",
165                  data->uid, data->gid, data->file_mode, data->dir_mode,options);
166         if (mount(share_name, ".", "smbfs", flags, data1) == 0)
167                 return 0;
168         return mount(share_name, ".", "smbfs", flags, data2);
169 }
170
171  int main(int argc, char *argv[])
172 {
173         char *mount_point, *share_name = NULL;
174         FILE *mtab;
175         int fd;
176         unsigned int flags;
177         struct smb_mount_data data;
178         struct mntent ment;
179
180         memset(&data, 0, sizeof(struct smb_mount_data));
181
182         if (argc < 2) {
183                 help();
184                 exit(1);
185         }
186
187         if (argv[1][0] == '-') {
188                 help();
189                 exit(1);
190         }
191
192         if (getuid() != 0) {
193                 user_mount = 1;
194         }
195
196         if (geteuid() != 0) {
197                 fprintf(stderr, "smbmnt must be installed suid root for direct user mounts (%d,%d)\n", getuid(), geteuid());
198                 exit(1);
199         }
200
201         mount_uid = getuid();
202         mount_gid = getgid();
203         mount_fmask = umask(0);
204         umask(mount_fmask);
205         mount_fmask = ~mount_fmask;
206
207         mount_point = fullpath(argv[1]);
208
209         argv += 1;
210         argc -= 1;
211
212         if (mount_ok(mount_point) != 0) {
213                 fprintf(stderr, "cannot mount on %s: %s\n",
214                         mount_point, strerror(errno));
215                 exit(1);
216         }
217
218         data.version = SMB_MOUNT_VERSION;
219
220         /* getuid() gives us the real uid, who may umount the fs */
221         data.mounted_uid = getuid();
222
223         if (parse_args(argc, argv, &data, &share_name) != 0) {
224                 help();
225                 return -1;
226         }
227
228         data.uid = mount_uid;
229         data.gid = mount_gid;
230         data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_fmask;
231         data.dir_mode  = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_dmask;
232
233         if (mount_dmask == 0) {
234                 data.dir_mode = data.file_mode;
235                 if ((data.dir_mode & S_IRUSR) != 0)
236                         data.dir_mode |= S_IXUSR;
237                 if ((data.dir_mode & S_IRGRP) != 0)
238                         data.dir_mode |= S_IXGRP;
239                 if ((data.dir_mode & S_IROTH) != 0)
240                         data.dir_mode |= S_IXOTH;
241         }
242
243         flags = MS_MGC_VAL;
244
245         if (mount_ro) flags |= MS_RDONLY;
246
247         if (do_mount(share_name, flags, &data) < 0) {
248                 switch (errno) {
249                 case ENODEV:
250                         fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n");
251                         break;
252                 default:
253                         perror("mount error");
254                 }
255                 fprintf(stderr, "Please refer to the smbmnt(8) manual page\n");
256                 return -1;
257         }
258
259         ment.mnt_fsname = share_name ? share_name : "none";
260         ment.mnt_dir = mount_point;
261         ment.mnt_type = "smbfs";
262         ment.mnt_opts = "";
263         ment.mnt_freq = 0;
264         ment.mnt_passno= 0;
265
266         mount_point = ment.mnt_dir;
267
268         if (mount_point == NULL)
269         {
270                 fprintf(stderr, "Mount point too long\n");
271                 return -1;
272         }
273         
274         if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
275         {
276                 fprintf(stderr, "Can't get "MOUNTED"~ lock file");
277                 return 1;
278         }
279         close(fd);
280         
281         if ((mtab = setmntent(MOUNTED, "a+")) == NULL)
282         {
283                 fprintf(stderr, "Can't open " MOUNTED);
284                 return 1;
285         }
286
287         if (addmntent(mtab, &ment) == 1)
288         {
289                 fprintf(stderr, "Can't write mount entry");
290                 return 1;
291         }
292         if (fchmod(fileno(mtab), 0644) == -1)
293         {
294                 fprintf(stderr, "Can't set perms on "MOUNTED);
295                 return 1;
296         }
297         endmntent(mtab);
298
299         if (unlink(MOUNTED"~") == -1)
300         {
301                 fprintf(stderr, "Can't remove "MOUNTED"~");
302                 return 1;
303         }
304
305         return 0;
306 }