diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-08 12:12:04 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-08 12:12:04 -0800 |
commit | 81153336eb76b253ba7852f3f1de525bb98f8c4d (patch) | |
tree | 635fe0495b762841eb72e5ee40d9d88563674691 | |
parent | ef9417e8a903d3a68a83ea2da32f1db030341c37 (diff) | |
parent | 4d673da14533b32fe8d3125b5b7be4fea14e39a8 (diff) |
Merge tag 'afs-next-20180208' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
Pull afs updates from David Howells:
"Four fixes:
- add a missing put
- two fixes to reset the address iteration cursor correctly
- fix setting up the fileserver iteration cursor.
Two cleanups:
- remove some dead code
- rearrange a function to be more logically laid out
And one new feature:
- Support AFS dynamic root.
With this one should be able to do, say:
mkdir /afs
mount -t afs none /afs -o dyn
to create a dynamic root and then, provided you have keyutils
installed, do:
ls /afs/grand.central.org
and:
ls /afs/umich.edu
to list the root volumes of both those organisations' AFS cells
without requiring any other setup (the kernel upcall to a program
in the keyutils package to do DNS access as does NFS)"
* tag 'afs-next-20180208' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
afs: Support the AFS dynamic root
afs: Rearrange afs_select_fileserver() a little
afs: Remove unused code
afs: Fix server list handling
afs: Need to clear responded flag in addr cursor
afs: Fix missing cursor clearance
afs: Add missing afs_put_cell()
-rw-r--r-- | Documentation/filesystems/afs.txt | 17 | ||||
-rw-r--r-- | fs/afs/addr_list.c | 13 | ||||
-rw-r--r-- | fs/afs/dir.c | 122 | ||||
-rw-r--r-- | fs/afs/inode.c | 48 | ||||
-rw-r--r-- | fs/afs/internal.h | 12 | ||||
-rw-r--r-- | fs/afs/mntpt.c | 20 | ||||
-rw-r--r-- | fs/afs/rotate.c | 293 | ||||
-rw-r--r-- | fs/afs/server_list.c | 3 | ||||
-rw-r--r-- | fs/afs/super.c | 132 | ||||
-rw-r--r-- | fs/afs/vlclient.c | 10 | ||||
-rw-r--r-- | fs/afs/volume.c | 47 | ||||
-rw-r--r-- | net/dns_resolver/dns_query.c | 22 |
12 files changed, 324 insertions, 415 deletions
diff --git a/Documentation/filesystems/afs.txt b/Documentation/filesystems/afs.txt index ba99b5ac4fd8..c5254f6d234d 100644 --- a/Documentation/filesystems/afs.txt +++ b/Documentation/filesystems/afs.txt @@ -7,6 +7,7 @@ Contents: - Overview. - Usage. - Mountpoints. + - Dynamic root. - Proc filesystem. - The cell database. - Security. @@ -127,6 +128,22 @@ mounted on /afs in one go by doing: umount /afs +============ +DYNAMIC ROOT +============ + +A mount option is available to create a serverless mount that is only usable +for dynamic lookup. Creating such a mount can be done by, for example: + + mount -t afs none /afs -o dyn + +This creates a mount that just has an empty directory at the root. Attempting +to look up a name in this directory will cause a mountpoint to be created that +looks up a cell of the same name, for example: + + ls /afs/grand.central.org/ + + =============== PROC FILESYSTEM =============== diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c index a537368ba0db..fd9f28b8a933 100644 --- a/fs/afs/addr_list.c +++ b/fs/afs/addr_list.c @@ -332,11 +332,18 @@ bool afs_iterate_addresses(struct afs_addr_cursor *ac) */ int afs_end_cursor(struct afs_addr_cursor *ac) { - if (ac->responded && ac->index != ac->start) - WRITE_ONCE(ac->alist->index, ac->index); + struct afs_addr_list *alist; + + alist = ac->alist; + if (alist) { + if (ac->responded && ac->index != ac->start) + WRITE_ONCE(alist->index, ac->index); + afs_put_addrlist(alist); + } - afs_put_addrlist(ac->alist); + ac->addr = NULL; ac->alist = NULL; + ac->begun = false; return ac->error; } diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 23c7f395d718..ba2b458b36d1 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -17,10 +17,13 @@ #include <linux/pagemap.h> #include <linux/ctype.h> #include <linux/sched.h> +#include <linux/dns_resolver.h> #include "internal.h" static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags); +static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags); static int afs_dir_open(struct inode *inode, struct file *file); static int afs_readdir(struct file *file, struct dir_context *ctx); static int afs_d_revalidate(struct dentry *dentry, unsigned int flags); @@ -64,6 +67,17 @@ const struct inode_operations afs_dir_inode_operations = { .listxattr = afs_listxattr, }; +const struct file_operations afs_dynroot_file_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .iterate_shared = dcache_readdir, + .llseek = dcache_dir_lseek, +}; + +const struct inode_operations afs_dynroot_inode_operations = { + .lookup = afs_dynroot_lookup, +}; + const struct dentry_operations afs_fs_dentry_operations = { .d_revalidate = afs_d_revalidate, .d_delete = afs_d_delete, @@ -468,25 +482,58 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, } /* + * Probe to see if a cell may exist. This prevents positive dentries from + * being created unnecessarily. + */ +static int afs_probe_cell_name(struct dentry *dentry) +{ + struct afs_cell *cell; + const char *name = dentry->d_name.name; + size_t len = dentry->d_name.len; + int ret; + + /* Names prefixed with a dot are R/W mounts. */ + if (name[0] == '.') { + if (len == 1) + return -EINVAL; + name++; + len--; + } + + cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len); + if (!IS_ERR(cell)) { + afs_put_cell(afs_d2net(dentry), cell); + return 0; + } + + ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL); + if (ret == -ENODATA) + ret = -EDESTADDRREQ; + return ret; +} + +/* * Try to auto mount the mountpoint with pseudo directory, if the autocell * operation is setted. */ -static struct inode *afs_try_auto_mntpt( - int ret, struct dentry *dentry, struct inode *dir, struct key *key, - struct afs_fid *fid) +static struct inode *afs_try_auto_mntpt(struct dentry *dentry, + struct inode *dir, struct afs_fid *fid) { - const char *devname = dentry->d_name.name; struct afs_vnode *vnode = AFS_FS_I(dir); struct inode *inode; + int ret = -ENOENT; - _enter("%d, %p{%pd}, {%x:%u}, %p", - ret, dentry, dentry, vnode->fid.vid, vnode->fid.vnode, key); + _enter("%p{%pd}, {%x:%u}", + dentry, dentry, vnode->fid.vid, vnode->fid.vnode); + + if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) + goto out; - if (ret != -ENOENT || - !test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) + ret = afs_probe_cell_name(dentry); + if (ret < 0) goto out; - inode = afs_iget_autocell(dir, devname, strlen(devname), key); + inode = afs_iget_pseudo_dir(dir->i_sb, false); if (IS_ERR(inode)) { ret = PTR_ERR(inode); goto out; @@ -545,13 +592,16 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ret = afs_do_lookup(dir, dentry, &fid, key); if (ret < 0) { - inode = afs_try_auto_mntpt(ret, dentry, dir, key, &fid); - if (!IS_ERR(inode)) { - key_put(key); - goto success; + if (ret == -ENOENT) { + inode = afs_try_auto_mntpt(dentry, dir, &fid); + if (!IS_ERR(inode)) { + key_put(key); + goto success; + } + + ret = PTR_ERR(inode); } - ret = PTR_ERR(inode); key_put(key); if (ret == -ENOENT) { d_add(dentry, NULL); @@ -583,12 +633,53 @@ success: } /* + * Look up an entry in a dynroot directory. + */ +static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct afs_vnode *vnode; + struct afs_fid fid; + struct inode *inode; + int ret; + + vnode = AFS_FS_I(dir); + + _enter("%pd", dentry); + + ASSERTCMP(d_inode(dentry), ==, NULL); + + if (dentry->d_name.len >= AFSNAMEMAX) { + _leave(" = -ENAMETOOLONG"); + return ERR_PTR(-ENAMETOOLONG); + } + + inode = afs_try_auto_mntpt(dentry, dir, &fid); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); + if (ret == -ENOENT) { + d_add(dentry, NULL); + _leave(" = NULL [negative]"); + return NULL; + } + _leave(" = %d [do]", ret); + return ERR_PTR(ret); + } + + d_add(dentry, inode); + _leave(" = 0 { ino=%lu v=%u }", + d_inode(dentry)->i_ino, d_inode(dentry)->i_generation); + return NULL; +} + +/* * check that a dentry lookup hit has found a valid entry * - NOTE! the hit can be a negative hit too, so we can't assume we have an * inode */ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) { + struct afs_super_info *as = dentry->d_sb->s_fs_info; struct afs_vnode *vnode, *dir; struct afs_fid uninitialized_var(fid); struct dentry *parent; @@ -600,6 +691,9 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) if (flags & LOOKUP_RCU) return -ECHILD; + if (as->dyn_root) + return 1; + if (d_really_is_positive(dentry)) { vnode = AFS_FS_I(d_inode(dentry)); _enter("{v={%x:%u} n=%pd fl=%lx},", diff --git a/fs/afs/inode.c b/fs/afs/inode.c index c7f17c44c7ce..6b39d0255b72 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -147,7 +147,7 @@ int afs_iget5_test(struct inode *inode, void *opaque) * * These pseudo inodes don't match anything. */ -static int afs_iget5_autocell_test(struct inode *inode, void *opaque) +static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque) { return 0; } @@ -169,31 +169,34 @@ static int afs_iget5_set(struct inode *inode, void *opaque) } /* - * inode retrieval for autocell + * Create an inode for a dynamic root directory or an autocell dynamic + * automount dir. */ -struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, - int namesz, struct key *key) +struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root) { struct afs_iget_data data; struct afs_super_info *as; struct afs_vnode *vnode; - struct super_block *sb; struct inode *inode; static atomic_t afs_autocell_ino; - _enter("{%x:%u},%*.*s,", - AFS_FS_I(dir)->fid.vid, AFS_FS_I(dir)->fid.vnode, - namesz, namesz, dev_name ?: ""); + _enter(""); - sb = dir->i_sb; as = sb->s_fs_info; - data.volume = as->volume; - data.fid.vid = as->volume->vid; - data.fid.unique = 0; - data.fid.vnode = 0; + if (as->volume) { + data.volume = as->volume; + data.fid.vid = as->volume->vid; + } + if (root) { + data.fid.vnode = 1; + data.fid.unique = 1; + } else { + data.fid.vnode = atomic_inc_return(&afs_autocell_ino); + data.fid.unique = 0; + } - inode = iget5_locked(sb, atomic_inc_return(&afs_autocell_ino), - afs_iget5_autocell_test, afs_iget5_set, + inode = iget5_locked(sb, data.fid.vnode, + afs_iget5_pseudo_dir_test, afs_iget5_set, &data); if (!inode) { _leave(" = -ENOMEM"); @@ -211,7 +214,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, inode->i_size = 0; inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; - inode->i_op = &afs_autocell_inode_operations; + if (root) { + inode->i_op = &afs_dynroot_inode_operations; + inode->i_fop = &afs_dynroot_file_operations; + } else { + inode->i_op = &afs_autocell_inode_operations; + } set_nlink(inode, 2); inode->i_uid = GLOBAL_ROOT_UID; inode->i_gid = GLOBAL_ROOT_GID; @@ -223,8 +231,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, inode->i_generation = 0; set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); - set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); - inode->i_flags |= S_AUTOMOUNT | S_NOATIME; + if (!root) { + set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); + inode->i_flags |= S_AUTOMOUNT; + } + + inode->i_flags |= S_NOATIME; unlock_new_inode(inode); _leave(" = %p", inode); return inode; diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 804d1f905622..f38d6a561a84 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -36,6 +36,7 @@ struct afs_mount_params { bool rwpath; /* T if the parent should be considered R/W */ bool force; /* T to force cell type */ bool autocell; /* T if set auto mount operation */ + bool dyn_root; /* T if dynamic root */ afs_voltype_t type; /* type of volume requested */ int volnamesz; /* size of volume name */ const char *volname; /* name of volume to mount */ @@ -186,6 +187,7 @@ struct afs_super_info { struct afs_net *net; /* Network namespace */ struct afs_cell *cell; /* The cell in which the volume resides */ struct afs_volume *volume; /* volume record */ + bool dyn_root; /* True if dynamic root */ }; static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) @@ -634,10 +636,13 @@ extern bool afs_cm_incoming_call(struct afs_call *); /* * dir.c */ -extern bool afs_dir_check_page(struct inode *, struct page *); +extern const struct file_operations afs_dir_file_operations; extern const struct inode_operations afs_dir_inode_operations; +extern const struct file_operations afs_dynroot_file_operations; +extern const struct inode_operations afs_dynroot_inode_operations; extern const struct dentry_operations afs_fs_dentry_operations; -extern const struct file_operations afs_dir_file_operations; + +extern bool afs_dir_check_page(struct inode *, struct page *); /* * file.c @@ -695,8 +700,7 @@ extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *, */ extern int afs_fetch_status(struct afs_vnode *, struct key *); extern int afs_iget5_test(struct inode *, void *); -extern struct inode *afs_iget_autocell(struct inode *, const char *, int, - struct key *); +extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool); extern struct inode *afs_iget(struct super_block *, struct key *, struct afs_fid *, struct afs_file_status *, struct afs_callback *, diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 690fea9d84c3..99fd13500a97 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -72,7 +72,7 @@ static int afs_mntpt_open(struct inode *inode, struct file *file) */ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) { - struct afs_super_info *super; + struct afs_super_info *as; struct vfsmount *mnt; struct afs_vnode *vnode; struct page *page; @@ -104,13 +104,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) goto error_no_page; if (mntpt->d_name.name[0] == '.') { - devname[0] = '#'; - memcpy(devname + 1, mntpt->d_name.name, size - 1); + devname[0] = '%'; + memcpy(devname + 1, mntpt->d_name.name + 1, size - 1); memcpy(devname + size, afs_root_cell, sizeof(afs_root_cell)); rwpath = true; } else { - devname[0] = '%'; + devname[0] = '#'; memcpy(devname + 1, mntpt->d_name.name, size); memcpy(devname + size + 1, afs_root_cell, sizeof(afs_root_cell)); @@ -142,11 +142,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) } /* work out what options we want */ - super = AFS_FS_S(mntpt->d_sb); - memcpy(options, "cell=", 5); - strcpy(options + 5, super->volume->cell->name); - if (super->volume->type == AFSVL_RWVOL || rwpath) - strcat(options, ",rwpath"); + as = AFS_FS_S(mntpt->d_sb); + if (as->cell) { + memcpy(options, "cell=", 5); + strcpy(options + 5, as->cell->name); + if ((as->volume && as->volume->type == AFSVL_RWVOL) || rwpath) + strcat(options, ",rwpath"); + } /* try and do the mount */ _debug("--- attempting mount %s -o %s ---", devname, options); diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c index d04511fb3879..ad1328d85526 100644 --- a/fs/afs/rotate.c +++ b/fs/afs/rotate.c @@ -330,26 +330,6 @@ start: if (!afs_start_fs_iteration(fc, vnode)) goto failed; - goto use_server; - -next_server: - _debug("next"); - afs_put_cb_interest(afs_v2net(vnode), fc->cbi); - fc->cbi = NULL; - fc->index++; - if (fc->index >= fc->server_list->nr_servers) - fc->index = 0; - if (fc->index != fc->start) - goto use_server; - - /* That's all the servers poked to no good effect. Try again if some - * of them were busy. - */ - if (fc->flags & AFS_FS_CURSOR_VBUSY) - goto restart_from_beginning; - - fc->ac.error = -EDESTADDRREQ; - goto failed; use_server: _debug("use"); @@ -383,6 +363,7 @@ use_server: afs_get_addrlist(alist); read_unlock(&server->fs_lock); + memset(&fc->ac, 0, sizeof(fc->ac)); /* Probe the current fileserver if we haven't done so yet. */ if (!test_bit(AFS_SERVER_FL_PROBED, &server->flags)) { @@ -397,12 +378,8 @@ use_server: else afs_put_addrlist(alist); - fc->ac.addr = NULL; fc->ac.start = READ_ONCE(alist->index); fc->ac.index = fc->ac.start; - fc->ac.error = 0; - fc->ac.begun = false; - goto iterate_address; iterate_address: ASSERT(fc->ac.alist); @@ -410,16 +387,35 @@ iterate_address: /* Iterate over the current server's address list to try and find an * address on which it will respond to us. */ - if (afs_iterate_addresses(&fc->ac)) { - _leave(" = t"); - return true; - } + if (!afs_iterate_addresses(&fc->ac)) + goto next_server; + + _leave(" = t"); + return true; +next_server: + _debug("next"); afs_end_cursor(&fc->ac); - goto next_server; + afs_put_cb_interest(afs_v2net(vnode), fc->cbi); + fc->cbi = NULL; + fc->index++; + if (fc->index >= fc->server_list->nr_servers) + fc->index = 0; + if (fc->index != fc->start) + goto use_server; + + /* That's all the servers poked to no good effect. Try again if some + * of them were busy. + */ + if (fc->flags & AFS_FS_CURSOR_VBUSY) + goto restart_from_beginning; + + fc->ac.error = -EDESTADDRREQ; + goto failed; failed: fc->flags |= AFS_FS_CURSOR_STOP; + afs_end_cursor(&fc->ac); _leave(" = f [failed %d]", fc->ac.error); return false; } @@ -458,12 +454,10 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc) return false; } + memset(&fc->ac, 0, sizeof(fc->ac)); fc->ac.alist = alist; - fc->ac.addr = NULL; fc->ac.start = READ_ONCE(alist->index); fc->ac.index = fc->ac.start; - fc->ac.error = 0; - fc->ac.begun = false; goto iterate_address; case 0: @@ -520,238 +514,3 @@ int afs_end_vnode_operation(struct afs_fs_cursor *fc) return fc->ac.error; } - -#if 0 -/* - * Set a filesystem server cursor for using a specific FS server. - */ -int afs_set_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode) -{ - afs_init_fs_cursor(fc, vnode); - - read_seqlock_excl(&vnode->cb_lock); - if (vnode->cb_interest) { - if (vnode->cb_interest->server->fs_state == 0) - fc->server = afs_get_server(vnode->cb_interest->server); - else - fc->ac.error = vnode->cb_interest->server->fs_state; - } else { - fc->ac.error = -ESTALE; - } - read_sequnlock_excl(&vnode->cb_lock); - - return fc->ac.error; -} - -/* - * pick a server to use to try accessing this volume - * - returns with an elevated usage count on the server chosen - */ -bool afs_volume_pick_fileserver(struct afs_fs_cursor *fc, struct afs_vnode *vnode) -{ - struct afs_volume *volume = vnode->volume; - struct afs_server *server; - int ret, state, loop; - - _enter("%s", volume->vlocation->vldb.name); - - /* stick with the server we're already using if we can */ - if (vnode->cb_interest && vnode->cb_interest->server->fs_state == 0) { - fc->server = afs_get_server(vnode->cb_interest->server); - goto set_server; - } - - down_read(&volume->server_sem); - - /* handle the no-server case */ - if (volume->nservers == 0) { - fc->ac.error = volume->rjservers ? -ENOMEDIUM : -ESTALE; - up_read(&volume->server_sem); - _leave(" = f [no servers %d]", fc->ac.error); - return false; - } - - /* basically, just search the list for the first live server and use - * that */ - ret = 0; - for (loop = 0; loop < volume->nservers; loop++) { - server = volume->servers[loop]; - state = server->fs_state; - - _debug("consider %d [%d]", loop, state); - - switch (state) { - case 0: - goto picked_server; - - case -ENETUNREACH: - if (ret == 0) - ret = state; - break; - - case -EHOSTUNREACH: - if (ret == 0 || - ret == -ENETUNREACH) - ret = state; - break; - - case -ECONNREFUSED: - if (ret == 0 || - ret == -ENETUNREACH || - ret == -EHOSTUNREACH) - ret = state; - break; - - default: - case -EREMOTEIO: - if (ret == 0 || - ret == -ENETUNREACH || - ret == -EHOSTUNREACH || - ret == -ECONNREFUSED) - ret = state; - break; - } - } - -error: - fc->ac.error = ret; - - /* no available servers - * - TODO: handle the no active servers case better - */ - up_read(&volume->server_sem); - _leave(" = f [%d]", fc->ac.error); - return false; - -picked_server: - /* Found an apparently healthy server. We need to register an interest - * in receiving callbacks before we talk to it. - */ - ret = afs_register_server_cb_interest(vnode, - &volume->cb_interests[loop], server); - if (ret < 0) - goto error; - - fc->server = afs_get_server(server); - up_read(&volume->server_sem); -set_server: - fc->ac.alist = afs_get_addrlist(fc->server->addrs); - fc->ac.addr = &fc->ac.alist->addrs[0]; - _debug("USING SERVER: %pIS\n", &fc->ac.addr->transport); - _leave(" = t (picked %pIS)", &fc->ac.addr->transport); - return true; -} - -/* - * release a server after use - * - releases the ref on the server struct that was acquired by picking - * - records result of using a particular server to access a volume - * - return true to try again, false if okay or to issue error - * - the caller must release the server struct if result was false - */ -bool afs_iterate_fs_cursor(struct afs_fs_cursor *fc, - struct afs_vnode *vnode) -{ - struct afs_volume *volume = vnode->volume; - struct afs_server *server = fc->server; - unsigned loop; - - _enter("%s,%pIS,%d", - volume->vlocation->vldb.name, &fc->ac.addr->transport, - fc->ac.error); - - switch (fc->ac.error) { - /* success */ - case 0: - server->fs_state = 0; - _leave(" = f"); - return false; - - /* the fileserver denied all knowledge of the volume */ - case -ENOMEDIUM: - down_write(&volume->server_sem); - - /* firstly, find where the server is in the active list (if it - * is) */ - for (loop = 0; loop < volume->nservers; loop++) - if (volume->servers[loop] == server) - goto present; - - /* no longer there - may have been discarded by another op */ - goto try_next_server_upw; - - present: - volume->nservers--; - memmove(&volume->servers[loop], - &volume->servers[loop + 1], - sizeof(volume->servers[loop]) * - (volume->nservers - loop)); - volume->servers[volume->nservers] = NULL; - afs_put_server(afs_v2net(vnode), server); - volume->rjservers++; - - if (volume->nservers > 0) - /* another server might acknowledge its existence */ - goto try_next_server_upw; - - /* handle the case where all the fileservers have rejected the - * volume - * - TODO: try asking the fileservers for volume information - * - TODO: contact the VL server again to see if the volume is - * no longer registered - */ - up_write(&volume->server_sem); - afs_put_server(afs_v2net(vnode), server); - fc->server = NULL; - _leave(" = f [completely rejected]"); - return false; - - /* problem reaching the server */ - case -ENETUNREACH: - case -EHOSTUNREACH: - case -ECONNREFUSED: - case -ETIME: - case -ETIMEDOUT: - case -EREMOTEIO: - /* mark the server as dead - * TODO: vary dead timeout depending on error - */ - spin_lock(&server->fs_lock); - if (!server->fs_state) { - server->fs_state = fc->ac.error; - printk("kAFS: SERVER DEAD state=%d\n", fc->ac.error); - } - spin_unlock(&server->fs_lock); - goto try_next_server; - - /* miscellaneous error */ - default: - case -ENOMEM: - case -ENONET: - /* tell the caller to accept the result */ - afs_put_server(afs_v2net(vnode), server); - fc->server = NULL; - _leave(" = f [local failure]"); - return false; - } - - /* tell the caller to loop around and try the next server */ -try_next_server_upw: - up_write(&volume->server_sem); -try_next_server: - afs_put_server(afs_v2net(vnode), server); - _leave(" = t [try next server]"); - return true; -} - -/* - * Clean up a fileserver cursor. - */ -int afs_end_fs_cursor(struct afs_fs_cursor *fc, struct afs_net *net) -{ - afs_end_cursor(&fc->ac); - afs_put_server(net, fc->server); - return fc->ac.error; -} - -#endif diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index 0ab3f8457839..0f8dc4c8f07c 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -58,7 +58,8 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell, server = afs_lookup_server(cell, key, &vldb->fs_server[i]); if (IS_ERR(server)) { ret = PTR_ERR(server); - if (ret == -ENOENT) + if (ret == -ENOENT || + ret == -ENOMEDIUM) continue; goto error_2; } diff --git a/fs/afs/super.c b/fs/afs/super.c index 1037dd41a622..3623c952b6ff 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -64,6 +64,7 @@ static atomic_t afs_count_active_inodes; enum { afs_no_opt, afs_opt_cell, + afs_opt_dyn, afs_opt_rwpath, afs_opt_vol, afs_opt_autocell, @@ -71,6 +72,7 @@ enum { static const match_table_t afs_options_list = { { afs_opt_cell, "cell=%s" }, + { afs_opt_dyn, "dyn" }, { afs_opt_rwpath, "rwpath" }, { afs_opt_vol, "vol=%s" }, { afs_opt_autocell, "autocell" }, @@ -148,6 +150,11 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) const char *suf = ""; char pref = '%'; + if (as->dyn_root) { + seq_puts(m, "none"); + return 0; + } + switch (volume->type) { case AFSVL_RWVOL: break; @@ -171,8 +178,12 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) */ static int afs_show_options(struct seq_file *m, struct dentry *root) { + struct afs_super_info *as = AFS_FS_S(root->d_sb); + + if (as->dyn_root) + seq_puts(m, ",dyn"); if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) - seq_puts(m, "autocell"); + seq_puts(m, ",autocell"); return 0; } @@ -212,7 +223,7 @@ static int afs_parse_options(struct afs_mount_params *params, break; case afs_opt_rwpath: - params->rwpath = 1; + params->rwpath = true; break; case afs_opt_vol: @@ -220,7 +231,11 @@ static int afs_parse_options(struct afs_mount_params *params, break; case afs_opt_autocell: - params->autocell = 1; + params->autocell = true; + break; + + case afs_opt_dyn: + params->dyn_root = true; break; default: @@ -254,7 +269,7 @@ static int afs_parse_device_name(struct afs_mount_params *params, int cellnamesz; _enter(",%s", name); - + if (!name) { printk(KERN_ERR "kAFS: no volume name specified\n"); return -EINVAL; @@ -336,7 +351,14 @@ static int afs_test_super(struct super_block *sb, void *data) struct afs_super_info *as1 = data; struct afs_super_info *as = AFS_FS_S(sb); - return as->net == as1->net && as->volume->vid == as1->volume->vid; + return (as->net == as1->net && + as->volume && + as->volume->vid == as1->volume->vid); +} + +static int afs_dynroot_test_super(struct super_block *sb, void *data) +{ + return false; } static int afs_set_super(struct super_block *sb, void *data) @@ -365,24 +387,30 @@ static int afs_fill_super(struct super_block *sb, sb->s_blocksize_bits = PAGE_SHIFT; sb->s_magic = AFS_FS_MAGIC; sb->s_op = &afs_super_ops; - sb->s_xattr = afs_xattr_handlers; + if (!as->dyn_root) + sb->s_xattr = afs_xattr_handlers; ret = super_setup_bdi(sb); if (ret) return ret; sb->s_bdi->ra_pages = VM_MAX_READAHEAD * 1024 / PAGE_SIZE; - sprintf(sb->s_id, "%u", as->volume->vid); - - afs_activate_volume(as->volume); /* allocate the root inode and dentry */ - fid.vid = as->volume->vid; - fid.vnode = 1; - fid.unique = 1; - inode = afs_iget(sb, params->key, &fid, NULL, NULL, NULL); + if (as->dyn_root) { + inode = afs_iget_pseudo_dir(sb, true); + sb->s_flags |= SB_RDONLY; + } else { + sprintf(sb->s_id, "%u", as->volume->vid); + afs_activate_volume(as->volume); + fid.vid = as->volume->vid; + fid.vnode = 1; + fid.unique = 1; + inode = afs_iget(sb, params->key, &fid, NULL, NULL, NULL); + } + if (IS_ERR(inode)) return PTR_ERR(inode); - if (params->autocell) + if (params->autocell || params->dyn_root) set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); ret = -ENOMEM; @@ -407,7 +435,10 @@ static struct afs_super_info *afs_alloc_sbi(struct afs_mount_params *params) as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); if (as) { as->net = afs_get_net(params->net); - as->cell = afs_get_cell(params->cell); + if (params->dyn_root) + as->dyn_root = true; + else + as->cell = afs_get_cell(params->cell); } return as; } @@ -451,18 +482,20 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, goto error; } - ret = afs_parse_device_name(¶ms, dev_name); - if (ret < 0) - goto error; + if (!params.dyn_root) { + ret = afs_parse_device_name(¶ms, dev_name); + if (ret < 0) + goto error; - /* try and do the mount securely */ - key = afs_request_key(params.cell); - if (IS_ERR(key)) { - _leave(" = %ld [key]", PTR_ERR(key)); - ret = PTR_ERR(key); - goto error; + /* try and do the mount securely */ + key = afs_request_key(params.cell); + if (IS_ERR(key)) { + _leave(" = %ld [key]", PTR_ERR(key)); + ret = PTR_ERR(key); + goto error; + } + params.key = key; } - params.key = key; /* allocate a superblock info record */ ret = -ENOMEM; @@ -470,20 +503,25 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, if (!as) goto error_key; - /* Assume we're going to need a volume record; at the very least we can - * use it to update the volume record if we have one already. This - * checks that the volume exists within the cell. - */ - candidate = afs_create_volume(¶ms); - if (IS_ERR(candidate)) { - ret = PTR_ERR(candidate); - goto error_as; - } + if (!params.dyn_root) { + /* Assume we're going to need a volume record; at the very + * least we can use it to update the volume record if we have + * one already. This checks that the volume exists within the + * cell. + */ + candidate = afs_create_volume(¶ms); + if (IS_ERR(candidate)) { + ret = PTR_ERR(candidate); + goto error_as; + } - as->volume = candidate; + as->volume = candidate; + } /* allocate a deviceless superblock */ - sb = sget(fs_type, afs_test_super, afs_set_super, flags, as); + sb = sget(fs_type, + as->dyn_root ? afs_dynroot_test_super : afs_test_super, + afs_set_super, flags, |