Hi Ludo, On Thu, 10 Jan 2019 09:50:49 +0100 Ludovic Courtès wrote: > Howdy! > > Danny Milosavljevic skribis: > > > On Tue, 08 Jan 2019 09:42:14 +0100 > > Ludovic Courtès wrote: > > > >> > Go has peculiar ideas of how the directory layout is supposed to be set up. > >> > I could probably figure it out - but if someone with more Go knowledge could > >> > step forward it would be much faster. > >> > >> I see Leo is Cc’d so we’ll see. :-) > > > > Nevermind, I've fixed it and learned something in the process: > > > > Linux doesn't actually know the current working directory as a string. > > It only knows the inode, so if you call getcwd, what libc actually does is > > it opendirs "..", then finds the entry with the same inode number as > > the current directory, and then returns the name of that entry. According to the POSIX standard ;) > Are you sure? In the Linux port of glibc I see this: > > --8<---------------cut here---------------start------------->8--- > char * > __getcwd (char *buf, size_t size) > { > char *path; > char *result; > > // […] > > retval = INLINE_SYSCALL (getcwd, 2, path, alloc_size); > --8<---------------cut here---------------end--------------->8--- > > And indeed, there’s a ‘getcwd’ syscall: > > --8<---------------cut here---------------start------------->8--- > $ strace -e getcwd guile -c '(getcwd)' > getcwd("/home/ludo", 100) = 11 > +++ exited with 0 +++ > --8<---------------cut here---------------end--------------->8--- Huh. I guess it boils down to whether the Linux "process" structure has the cwd in it as a string or as an inode. In Linux sources: static inline void get_fs_pwd(struct fs_struct *fs, struct path *pwd) { spin_lock(&fs->lock); *pwd = fs->pwd; path_get(pwd); spin_unlock(&fs->lock); } static void get_fs_root_and_pwd_rcu(struct fs_struct *fs, struct path *root, struct path *pwd) { unsigned seq; do { seq = read_seqcount_begin(&fs->seq); *root = fs->root; *pwd = fs->pwd; } while (read_seqcount_retry(&fs->seq, seq)); } struct path { struct vfsmount *mnt; struct dentry *dentry; } __randomize_layout; SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) { int error; struct path pwd, root; char *page = __getname(); if (!page) return -ENOMEM; rcu_read_lock(); get_fs_root_and_pwd_rcu(current->fs, &root, &pwd); error = -ENOENT; if (!d_unlinked(pwd.dentry)) { unsigned long len; char *cwd = page + PATH_MAX; int buflen = PATH_MAX; prepend(&cwd, &buflen, "\0", 1); error = prepend_path(&pwd, &root, &cwd, &buflen); rcu_read_unlock(); if (error < 0) goto out; /* Unreachable from current root */ if (error > 0) { error = prepend_unreachable(&cwd, &buflen); if (error) goto out; } error = -ERANGE; len = PATH_MAX + page - cwd; if (len <= size) { error = len; if (copy_to_user(buf, cwd, len)) error = -EFAULT; } } else { rcu_read_unlock(); } out: __putname(page); return error; } /* * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values. * It can block. */ void set_fs_pwd(struct fs_struct *fs, const struct path *path) { struct path old_pwd; path_get(path); spin_lock(&fs->lock); write_seqcount_begin(&fs->seq); old_pwd = fs->pwd; fs->pwd = *path; <----------------- !!!! write_seqcount_end(&fs->seq); spin_unlock(&fs->lock); if (old_pwd.dentry) path_put(&old_pwd); } int ksys_chdir(const char __user *filename) { struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; retry: error = user_path_at(AT_FDCWD, filename, lookup_flags, &path); if (error) goto out; error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; set_fs_pwd(current->fs, &path); <----------------- !!! dput_and_out: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } out: return error; } SYSCALL_DEFINE1(chdir, const char __user *, filename) { return ksys_chdir(filename); } SYSCALL_DEFINE1(fchdir, unsigned int, fd) { struct fd f = fdget_raw(fd); int error; error = -EBADF; if (!f.file) goto out; error = -ENOTDIR; if (!d_can_lookup(f.file->f_path.dentry)) goto out_putf; error = inode_permission(file_inode(f.file), MAY_EXEC | MAY_CHDIR); if (!error) set_fs_pwd(current->fs, &f.file->f_path); out_putf: fdput(f); out: return error; } Interesting! > Thanks for taking the time to address this! No problem :)