• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   fuse subdir module: offset paths with a base directory
3   Copyright (C) 2007  Miklos Szeredi <miklos@szeredi.hu>
4 
5   This program can be distributed under the terms of the GNU LGPLv2.
6   See the file COPYING.LIB
7 */
8 
9 #include <fuse_config.h>
10 
11 #include <fuse.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <string.h>
16 #include <errno.h>
17 
18 struct subdir {
19 	char *base;
20 	size_t baselen;
21 	int rellinks;
22 	struct fuse_fs *next;
23 };
24 
subdir_get(void)25 static struct subdir *subdir_get(void)
26 {
27 	return fuse_get_context()->private_data;
28 }
29 
subdir_addpath(struct subdir * d,const char * path,char ** newpathp)30 static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
31 {
32 	char *newpath = NULL;
33 
34 	if (path != NULL) {
35 		unsigned newlen = d->baselen + strlen(path);
36 
37 		newpath = malloc(newlen + 2);
38 		if (!newpath)
39 			return -ENOMEM;
40 
41 		if (path[0] == '/')
42 			path++;
43 		strcpy(newpath, d->base);
44 		strcpy(newpath + d->baselen, path);
45 		if (!newpath[0])
46 			strcpy(newpath, ".");
47 	}
48 	*newpathp = newpath;
49 
50 	return 0;
51 }
52 
subdir_getattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)53 static int subdir_getattr(const char *path, struct stat *stbuf,
54 			  struct fuse_file_info *fi)
55 {
56 	struct subdir *d = subdir_get();
57 	char *newpath;
58 	int err = subdir_addpath(d, path, &newpath);
59 	if (!err) {
60 		err = fuse_fs_getattr(d->next, newpath, stbuf, fi);
61 		free(newpath);
62 	}
63 	return err;
64 }
65 
subdir_access(const char * path,int mask)66 static int subdir_access(const char *path, int mask)
67 {
68 	struct subdir *d = subdir_get();
69 	char *newpath;
70 	int err = subdir_addpath(d, path, &newpath);
71 	if (!err) {
72 		err = fuse_fs_access(d->next, newpath, mask);
73 		free(newpath);
74 	}
75 	return err;
76 }
77 
78 
count_components(const char * p)79 static int count_components(const char *p)
80 {
81 	int ctr;
82 
83 	for (; *p == '/'; p++);
84 	for (ctr = 0; *p; ctr++) {
85 		for (; *p && *p != '/'; p++);
86 		for (; *p == '/'; p++);
87 	}
88 	return ctr;
89 }
90 
strip_common(const char ** sp,const char ** tp)91 static void strip_common(const char **sp, const char **tp)
92 {
93 	const char *s = *sp;
94 	const char *t = *tp;
95 	do {
96 		for (; *s == '/'; s++);
97 		for (; *t == '/'; t++);
98 		*tp = t;
99 		*sp = s;
100 		for (; *s == *t && *s && *s != '/'; s++, t++);
101 	} while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
102 }
103 
transform_symlink(struct subdir * d,const char * path,char * buf,size_t size)104 static void transform_symlink(struct subdir *d, const char *path,
105 			      char *buf, size_t size)
106 {
107 	const char *l = buf;
108 	size_t llen;
109 	char *s;
110 	int dotdots;
111 	int i;
112 
113 	if (l[0] != '/' || d->base[0] != '/')
114 		return;
115 
116 	strip_common(&l, &path);
117 	if (l - buf < (long) d->baselen)
118 		return;
119 
120 	dotdots = count_components(path);
121 	if (!dotdots)
122 		return;
123 	dotdots--;
124 
125 	llen = strlen(l);
126 	if (dotdots * 3 + llen + 2 > size)
127 		return;
128 
129 	s = buf + dotdots * 3;
130 	if (llen)
131 		memmove(s, l, llen + 1);
132 	else if (!dotdots)
133 		strcpy(s, ".");
134 	else
135 		*s = '\0';
136 
137 	for (s = buf, i = 0; i < dotdots; i++, s += 3)
138 		memcpy(s, "../", 3);
139 }
140 
141 
subdir_readlink(const char * path,char * buf,size_t size)142 static int subdir_readlink(const char *path, char *buf, size_t size)
143 {
144 	struct subdir *d = subdir_get();
145 	char *newpath;
146 	int err = subdir_addpath(d, path, &newpath);
147 	if (!err) {
148 		err = fuse_fs_readlink(d->next, newpath, buf, size);
149 		if (!err && d->rellinks)
150 			transform_symlink(d, newpath, buf, size);
151 		free(newpath);
152 	}
153 	return err;
154 }
155 
subdir_opendir(const char * path,struct fuse_file_info * fi)156 static int subdir_opendir(const char *path, struct fuse_file_info *fi)
157 {
158 	struct subdir *d = subdir_get();
159 	char *newpath;
160 	int err = subdir_addpath(d, path, &newpath);
161 	if (!err) {
162 		err = fuse_fs_opendir(d->next, newpath, fi);
163 		free(newpath);
164 	}
165 	return err;
166 }
167 
subdir_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi,enum fuse_readdir_flags flags)168 static int subdir_readdir(const char *path, void *buf,
169 			  fuse_fill_dir_t filler, off_t offset,
170 			  struct fuse_file_info *fi,
171 			  enum fuse_readdir_flags flags)
172 {
173 	struct subdir *d = subdir_get();
174 	char *newpath;
175 	int err = subdir_addpath(d, path, &newpath);
176 	if (!err) {
177 		err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
178 				      fi, flags);
179 		free(newpath);
180 	}
181 	return err;
182 }
183 
subdir_releasedir(const char * path,struct fuse_file_info * fi)184 static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
185 {
186 	struct subdir *d = subdir_get();
187 	char *newpath;
188 	int err = subdir_addpath(d, path, &newpath);
189 	if (!err) {
190 		err = fuse_fs_releasedir(d->next, newpath, fi);
191 		free(newpath);
192 	}
193 	return err;
194 }
195 
subdir_mknod(const char * path,mode_t mode,dev_t rdev)196 static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
197 {
198 	struct subdir *d = subdir_get();
199 	char *newpath;
200 	int err = subdir_addpath(d, path, &newpath);
201 	if (!err) {
202 		err = fuse_fs_mknod(d->next, newpath, mode, rdev);
203 		free(newpath);
204 	}
205 	return err;
206 }
207 
subdir_mkdir(const char * path,mode_t mode)208 static int subdir_mkdir(const char *path, mode_t mode)
209 {
210 	struct subdir *d = subdir_get();
211 	char *newpath;
212 	int err = subdir_addpath(d, path, &newpath);
213 	if (!err) {
214 		err = fuse_fs_mkdir(d->next, newpath, mode);
215 		free(newpath);
216 	}
217 	return err;
218 }
219 
subdir_unlink(const char * path)220 static int subdir_unlink(const char *path)
221 {
222 	struct subdir *d = subdir_get();
223 	char *newpath;
224 	int err = subdir_addpath(d, path, &newpath);
225 	if (!err) {
226 		err = fuse_fs_unlink(d->next, newpath);
227 		free(newpath);
228 	}
229 	return err;
230 }
231 
subdir_rmdir(const char * path)232 static int subdir_rmdir(const char *path)
233 {
234 	struct subdir *d = subdir_get();
235 	char *newpath;
236 	int err = subdir_addpath(d, path, &newpath);
237 	if (!err) {
238 		err = fuse_fs_rmdir(d->next, newpath);
239 		free(newpath);
240 	}
241 	return err;
242 }
243 
subdir_symlink(const char * from,const char * path)244 static int subdir_symlink(const char *from, const char *path)
245 {
246 	struct subdir *d = subdir_get();
247 	char *newpath;
248 	int err = subdir_addpath(d, path, &newpath);
249 	if (!err) {
250 		err = fuse_fs_symlink(d->next, from, newpath);
251 		free(newpath);
252 	}
253 	return err;
254 }
255 
subdir_rename(const char * from,const char * to,unsigned int flags)256 static int subdir_rename(const char *from, const char *to, unsigned int flags)
257 {
258 	struct subdir *d = subdir_get();
259 	char *newfrom;
260 	char *newto;
261 	int err = subdir_addpath(d, from, &newfrom);
262 	if (!err) {
263 		err = subdir_addpath(d, to, &newto);
264 		if (!err) {
265 			err = fuse_fs_rename(d->next, newfrom, newto, flags);
266 			free(newto);
267 		}
268 		free(newfrom);
269 	}
270 	return err;
271 }
272 
subdir_link(const char * from,const char * to)273 static int subdir_link(const char *from, const char *to)
274 {
275 	struct subdir *d = subdir_get();
276 	char *newfrom;
277 	char *newto;
278 	int err = subdir_addpath(d, from, &newfrom);
279 	if (!err) {
280 		err = subdir_addpath(d, to, &newto);
281 		if (!err) {
282 			err = fuse_fs_link(d->next, newfrom, newto);
283 			free(newto);
284 		}
285 		free(newfrom);
286 	}
287 	return err;
288 }
289 
subdir_chmod(const char * path,mode_t mode,struct fuse_file_info * fi)290 static int subdir_chmod(const char *path, mode_t mode,
291 			struct fuse_file_info *fi)
292 {
293 	struct subdir *d = subdir_get();
294 	char *newpath;
295 	int err = subdir_addpath(d, path, &newpath);
296 	if (!err) {
297 		err = fuse_fs_chmod(d->next, newpath, mode, fi);
298 		free(newpath);
299 	}
300 	return err;
301 }
302 
subdir_chown(const char * path,uid_t uid,gid_t gid,struct fuse_file_info * fi)303 static int subdir_chown(const char *path, uid_t uid, gid_t gid,
304 			struct fuse_file_info *fi)
305 {
306 	struct subdir *d = subdir_get();
307 	char *newpath;
308 	int err = subdir_addpath(d, path, &newpath);
309 	if (!err) {
310 		err = fuse_fs_chown(d->next, newpath, uid, gid, fi);
311 		free(newpath);
312 	}
313 	return err;
314 }
315 
subdir_truncate(const char * path,off_t size,struct fuse_file_info * fi)316 static int subdir_truncate(const char *path, off_t size,
317 			   struct fuse_file_info *fi)
318 {
319 	struct subdir *d = subdir_get();
320 	char *newpath;
321 	int err = subdir_addpath(d, path, &newpath);
322 	if (!err) {
323 		err = fuse_fs_truncate(d->next, newpath, size, fi);
324 		free(newpath);
325 	}
326 	return err;
327 }
328 
subdir_utimens(const char * path,const struct timespec ts[2],struct fuse_file_info * fi)329 static int subdir_utimens(const char *path, const struct timespec ts[2],
330 			  struct fuse_file_info *fi)
331 {
332 	struct subdir *d = subdir_get();
333 	char *newpath;
334 	int err = subdir_addpath(d, path, &newpath);
335 	if (!err) {
336 		err = fuse_fs_utimens(d->next, newpath, ts, fi);
337 		free(newpath);
338 	}
339 	return err;
340 }
341 
subdir_create(const char * path,mode_t mode,struct fuse_file_info * fi)342 static int subdir_create(const char *path, mode_t mode,
343 			 struct fuse_file_info *fi)
344 {
345 	struct subdir *d = subdir_get();
346 	char *newpath;
347 	int err = subdir_addpath(d, path, &newpath);
348 	if (!err) {
349 		err = fuse_fs_create(d->next, newpath, mode, fi);
350 		free(newpath);
351 	}
352 	return err;
353 }
354 
subdir_open(const char * path,struct fuse_file_info * fi)355 static int subdir_open(const char *path, struct fuse_file_info *fi)
356 {
357 	struct subdir *d = subdir_get();
358 	char *newpath;
359 	int err = subdir_addpath(d, path, &newpath);
360 	if (!err) {
361 		err = fuse_fs_open(d->next, newpath, fi);
362 		free(newpath);
363 	}
364 	return err;
365 }
366 
subdir_read_buf(const char * path,struct fuse_bufvec ** bufp,size_t size,off_t offset,struct fuse_file_info * fi)367 static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
368 			   size_t size, off_t offset, struct fuse_file_info *fi)
369 {
370 	struct subdir *d = subdir_get();
371 	char *newpath;
372 	int err = subdir_addpath(d, path, &newpath);
373 	if (!err) {
374 		err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
375 		free(newpath);
376 	}
377 	return err;
378 }
379 
subdir_write_buf(const char * path,struct fuse_bufvec * buf,off_t offset,struct fuse_file_info * fi)380 static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
381 			off_t offset, struct fuse_file_info *fi)
382 {
383 	struct subdir *d = subdir_get();
384 	char *newpath;
385 	int err = subdir_addpath(d, path, &newpath);
386 	if (!err) {
387 		err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
388 		free(newpath);
389 	}
390 	return err;
391 }
392 
subdir_statfs(const char * path,struct statvfs * stbuf)393 static int subdir_statfs(const char *path, struct statvfs *stbuf)
394 {
395 	struct subdir *d = subdir_get();
396 	char *newpath;
397 	int err = subdir_addpath(d, path, &newpath);
398 	if (!err) {
399 		err = fuse_fs_statfs(d->next, newpath, stbuf);
400 		free(newpath);
401 	}
402 	return err;
403 }
404 
subdir_flush(const char * path,struct fuse_file_info * fi)405 static int subdir_flush(const char *path, struct fuse_file_info *fi)
406 {
407 	struct subdir *d = subdir_get();
408 	char *newpath;
409 	int err = subdir_addpath(d, path, &newpath);
410 	if (!err) {
411 		err = fuse_fs_flush(d->next, newpath, fi);
412 		free(newpath);
413 	}
414 	return err;
415 }
416 
subdir_release(const char * path,struct fuse_file_info * fi)417 static int subdir_release(const char *path, struct fuse_file_info *fi)
418 {
419 	struct subdir *d = subdir_get();
420 	char *newpath;
421 	int err = subdir_addpath(d, path, &newpath);
422 	if (!err) {
423 		err = fuse_fs_release(d->next, newpath, fi);
424 		free(newpath);
425 	}
426 	return err;
427 }
428 
subdir_fsync(const char * path,int isdatasync,struct fuse_file_info * fi)429 static int subdir_fsync(const char *path, int isdatasync,
430 			struct fuse_file_info *fi)
431 {
432 	struct subdir *d = subdir_get();
433 	char *newpath;
434 	int err = subdir_addpath(d, path, &newpath);
435 	if (!err) {
436 		err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
437 		free(newpath);
438 	}
439 	return err;
440 }
441 
subdir_fsyncdir(const char * path,int isdatasync,struct fuse_file_info * fi)442 static int subdir_fsyncdir(const char *path, int isdatasync,
443 			   struct fuse_file_info *fi)
444 {
445 	struct subdir *d = subdir_get();
446 	char *newpath;
447 	int err = subdir_addpath(d, path, &newpath);
448 	if (!err) {
449 		err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
450 		free(newpath);
451 	}
452 	return err;
453 }
454 
subdir_setxattr(const char * path,const char * name,const char * value,size_t size,int flags)455 static int subdir_setxattr(const char *path, const char *name,
456 			   const char *value, size_t size, int flags)
457 {
458 	struct subdir *d = subdir_get();
459 	char *newpath;
460 	int err = subdir_addpath(d, path, &newpath);
461 	if (!err) {
462 		err = fuse_fs_setxattr(d->next, newpath, name, value, size,
463 				       flags);
464 		free(newpath);
465 	}
466 	return err;
467 }
468 
subdir_getxattr(const char * path,const char * name,char * value,size_t size)469 static int subdir_getxattr(const char *path, const char *name, char *value,
470 			   size_t size)
471 {
472 	struct subdir *d = subdir_get();
473 	char *newpath;
474 	int err = subdir_addpath(d, path, &newpath);
475 	if (!err) {
476 		err = fuse_fs_getxattr(d->next, newpath, name, value, size);
477 		free(newpath);
478 	}
479 	return err;
480 }
481 
subdir_listxattr(const char * path,char * list,size_t size)482 static int subdir_listxattr(const char *path, char *list, size_t size)
483 {
484 	struct subdir *d = subdir_get();
485 	char *newpath;
486 	int err = subdir_addpath(d, path, &newpath);
487 	if (!err) {
488 		err = fuse_fs_listxattr(d->next, newpath, list, size);
489 		free(newpath);
490 	}
491 	return err;
492 }
493 
subdir_removexattr(const char * path,const char * name)494 static int subdir_removexattr(const char *path, const char *name)
495 {
496 	struct subdir *d = subdir_get();
497 	char *newpath;
498 	int err = subdir_addpath(d, path, &newpath);
499 	if (!err) {
500 		err = fuse_fs_removexattr(d->next, newpath, name);
501 		free(newpath);
502 	}
503 	return err;
504 }
505 
subdir_lock(const char * path,struct fuse_file_info * fi,int cmd,struct flock * lock)506 static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
507 		       struct flock *lock)
508 {
509 	struct subdir *d = subdir_get();
510 	char *newpath;
511 	int err = subdir_addpath(d, path, &newpath);
512 	if (!err) {
513 		err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
514 		free(newpath);
515 	}
516 	return err;
517 }
518 
subdir_flock(const char * path,struct fuse_file_info * fi,int op)519 static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
520 {
521 	struct subdir *d = subdir_get();
522 	char *newpath;
523 	int err = subdir_addpath(d, path, &newpath);
524 	if (!err) {
525 		err = fuse_fs_flock(d->next, newpath, fi, op);
526 		free(newpath);
527 	}
528 	return err;
529 }
530 
subdir_bmap(const char * path,size_t blocksize,uint64_t * idx)531 static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
532 {
533 	struct subdir *d = subdir_get();
534 	char *newpath;
535 	int err = subdir_addpath(d, path, &newpath);
536 	if (!err) {
537 		err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
538 		free(newpath);
539 	}
540 	return err;
541 }
542 
subdir_lseek(const char * path,off_t off,int whence,struct fuse_file_info * fi)543 static off_t subdir_lseek(const char *path, off_t off, int whence,
544 			  struct fuse_file_info *fi)
545 {
546 	struct subdir *ic = subdir_get();
547 	char *newpath;
548 	int res = subdir_addpath(ic, path, &newpath);
549 	if (!res) {
550 		res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
551 		free(newpath);
552 	}
553 	return res;
554 }
555 
subdir_init(struct fuse_conn_info * conn,struct fuse_config * cfg)556 static void *subdir_init(struct fuse_conn_info *conn,
557 			 struct fuse_config *cfg)
558 {
559 	struct subdir *d = subdir_get();
560 	fuse_fs_init(d->next, conn, cfg);
561 	/* Don't touch cfg->nullpath_ok, we can work with
562 	   either */
563 	return d;
564 }
565 
subdir_destroy(void * data)566 static void subdir_destroy(void *data)
567 {
568 	struct subdir *d = data;
569 	fuse_fs_destroy(d->next);
570 	free(d->base);
571 	free(d);
572 }
573 
574 static const struct fuse_operations subdir_oper = {
575 	.destroy	= subdir_destroy,
576 	.init		= subdir_init,
577 	.getattr	= subdir_getattr,
578 	.access		= subdir_access,
579 	.readlink	= subdir_readlink,
580 	.opendir	= subdir_opendir,
581 	.readdir	= subdir_readdir,
582 	.releasedir	= subdir_releasedir,
583 	.mknod		= subdir_mknod,
584 	.mkdir		= subdir_mkdir,
585 	.symlink	= subdir_symlink,
586 	.unlink		= subdir_unlink,
587 	.rmdir		= subdir_rmdir,
588 	.rename		= subdir_rename,
589 	.link		= subdir_link,
590 	.chmod		= subdir_chmod,
591 	.chown		= subdir_chown,
592 	.truncate	= subdir_truncate,
593 	.utimens	= subdir_utimens,
594 	.create		= subdir_create,
595 	.open		= subdir_open,
596 	.read_buf	= subdir_read_buf,
597 	.write_buf	= subdir_write_buf,
598 	.statfs		= subdir_statfs,
599 	.flush		= subdir_flush,
600 	.release	= subdir_release,
601 	.fsync		= subdir_fsync,
602 	.fsyncdir	= subdir_fsyncdir,
603 	.setxattr	= subdir_setxattr,
604 	.getxattr	= subdir_getxattr,
605 	.listxattr	= subdir_listxattr,
606 	.removexattr	= subdir_removexattr,
607 	.lock		= subdir_lock,
608 	.flock		= subdir_flock,
609 	.bmap		= subdir_bmap,
610 	.lseek		= subdir_lseek,
611 };
612 
613 static const struct fuse_opt subdir_opts[] = {
614 	FUSE_OPT_KEY("-h", 0),
615 	FUSE_OPT_KEY("--help", 0),
616 	{ "subdir=%s", offsetof(struct subdir, base), 0 },
617 	{ "rellinks", offsetof(struct subdir, rellinks), 1 },
618 	{ "norellinks", offsetof(struct subdir, rellinks), 0 },
619 	FUSE_OPT_END
620 };
621 
subdir_help(void)622 static void subdir_help(void)
623 {
624 	printf(
625 "    -o subdir=DIR	    prepend this directory to all paths (mandatory)\n"
626 "    -o [no]rellinks	    transform absolute symlinks to relative\n");
627 }
628 
subdir_opt_proc(void * data,const char * arg,int key,struct fuse_args * outargs)629 static int subdir_opt_proc(void *data, const char *arg, int key,
630 			   struct fuse_args *outargs)
631 {
632 	(void) data; (void) arg; (void) outargs;
633 
634 	if (!key) {
635 		subdir_help();
636 		return -1;
637 	}
638 
639 	return 1;
640 }
641 
subdir_new(struct fuse_args * args,struct fuse_fs * next[])642 static struct fuse_fs *subdir_new(struct fuse_args *args,
643 				  struct fuse_fs *next[])
644 {
645 	struct fuse_fs *fs;
646 	struct subdir *d;
647 
648 	d = calloc(1, sizeof(struct subdir));
649 	if (d == NULL) {
650 		fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
651 		return NULL;
652 	}
653 
654 	if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
655 		goto out_free;
656 
657 	if (!next[0] || next[1]) {
658 		fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
659 		goto out_free;
660 	}
661 
662 	if (!d->base) {
663 		fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
664 		goto out_free;
665 	}
666 
667 	if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
668 		char *tmp = realloc(d->base, strlen(d->base) + 2);
669 		if (!tmp) {
670 			fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
671 			goto out_free;
672 		}
673 		d->base = tmp;
674 		strcat(d->base, "/");
675 	}
676 	d->baselen = strlen(d->base);
677 	d->next = next[0];
678 	fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
679 	if (!fs)
680 		goto out_free;
681 	return fs;
682 
683 out_free:
684 	free(d->base);
685 	free(d);
686 	return NULL;
687 }
688 
689 FUSE_REGISTER_MODULE(subdir, subdir_new);
690